Merge branch 'develop' of https://github.com/XLabsProject/iw4x-client into feature/xinput

This commit is contained in:
rackover 2021-05-02 11:47:33 +02:00
commit 332232b88f
23 changed files with 1366 additions and 200 deletions

View File

@ -4,7 +4,7 @@
![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg)
[![build status](https://ci.appveyor.com/api/projects/status/rvljq0ooxen0oexm/branch/develop?svg=true)](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop)
[![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3)
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/iw4x)
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/xlabsproject)
# IW4x: Client

View File

@ -456,10 +456,7 @@ workspace "iw4x"
--iw4mvm.project()
workspace "*"
buildoptions {
"/std:c++latest"
}
systemversion "latest"
cppdialect "C++17"
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
rule "ProtobufCompiler"

View File

@ -7,12 +7,13 @@ namespace Assets
void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
}
void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
{
switch (elemType)
{
case Game::FX_ELEM_TYPE_MODEL:
@ -34,10 +35,7 @@ namespace Assets
if (visuals->soundName)
{
visuals->soundName = reader->readCString();
visuals->soundName = "null";
Components::Logger::Print("Unable to load sounds yet!\n");
}
break;
}

View File

@ -4,7 +4,7 @@ namespace Assets
{
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File soundFile(Utils::String::VA("sounds/%s", name.data()));
Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data()));
if (!soundFile.exists())
{
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
@ -60,8 +60,8 @@ namespace Assets
}
sound->sound.info.channels = reader.read<short>();
sound->sound.info.samples = reader.read<int>();
sound->sound.info.rate = reader.read<int>();
sound->sound.info.samples = reader.read<int>();
sound->sound.info.block_size = reader.read<short>();
sound->sound.info.bits = reader.read<short>();

View File

@ -29,6 +29,33 @@ namespace Assets
"_add_lin_nofog",
};
std::map<std::string, std::string> techSetCorrespondance = {
{"effect", "effect_blend"},
{"effect", "effect_blend"},
{"effect_nofog", "effect_blend_nofog"},
{"effect_zfeather", "effect_zfeather_blend"},
{"wc_unlit_add", "wc_unlit_add_lin"},
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
{"wc_unlit", "wc_unlit_add_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin_ua"},
{"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"},
{"wc_unlit_blend", "wc_unlit_blend_lin"},
{"wc_unlit_replace", "wc_unlit_replace_lin"},
{"mc_unlit_replace", "mc_unlit_replace_lin"},
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
{"mc_unlit", "mc_unlit_blend_lin"},
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
/*,
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},*/
};
Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data()));
if (!materialFile.exists()) return;
@ -185,12 +212,14 @@ namespace Assets
if (!t1->techniques[i] && !t2->techniques[i]) continue;;
if (!t1->techniques[i] || !t2->techniques[i]) return false;
if (t1->techniques[i]->flags != t1->techniques[i]->flags) return false;
// Apparently, this is really not that important
//if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false;
}
return true;
};
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
{
if (!replacementFound)
@ -209,8 +238,52 @@ namespace Assets
if (!replacementFound && asset->techniqueSet)
{
Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name);
std::string techName = asset->techniqueSet->name;
if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) {
auto iw4TechSetName = techSetCorrespondance[techName];
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
if (iw4TechSet)
{
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry)
{
if (!replacementFound)
{
Game::XAssetHeader header = entry->asset.header;
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet)
{
Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!)\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
asset->info.sortKey = header.material->info.sortKey;
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
// this is terrible!
asset->stateBitsCount = header.material->stateBitsCount;
asset->stateBitsTable = header.material->stateBitsTable;
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
asset->constantCount = header.material->constantCount;
asset->constantTable = header.material->constantTable;
replacementFound = true;
}
}
}, false, false);
if (!replacementFound)
{
Components::Logger::Print("Could not find any loaded material with techset %s (in replacement of %s), so I cannot set the sortkey for material %s\n", iw4TechSetName.data(), asset->techniqueSet->name, asset->info.name);
}
}
else
{
Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName.data(), asset->techniqueSet->name);
}
}
else
{
Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName.data());
}
}
if (!reader.end())

View File

@ -119,6 +119,79 @@ namespace Assets
if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect);
if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect);
if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect);
#define LoadWeapSound(sound) if (asset->weapDef->##sound##) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->weapDef->##sound##)
LoadWeapSound(pickupSound);
LoadWeapSound(pickupSoundPlayer);
LoadWeapSound(ammoPickupSound);
LoadWeapSound(ammoPickupSoundPlayer);
LoadWeapSound(projectileSound);
LoadWeapSound(pullbackSound);
LoadWeapSound(pullbackSoundPlayer);
LoadWeapSound(fireSound);
LoadWeapSound(fireSoundPlayer);
LoadWeapSound(fireSoundPlayerAkimbo);
LoadWeapSound(fireLoopSound);
LoadWeapSound(fireLoopSoundPlayer);
LoadWeapSound(fireStopSound);
LoadWeapSound(fireStopSoundPlayer);
LoadWeapSound(fireLastSound);
LoadWeapSound(fireLastSoundPlayer);
LoadWeapSound(emptyFireSound);
LoadWeapSound(emptyFireSoundPlayer);
LoadWeapSound(meleeSwipeSound);
LoadWeapSound(meleeSwipeSoundPlayer);
LoadWeapSound(meleeHitSound);
LoadWeapSound(meleeMissSound);
LoadWeapSound(rechamberSound);
LoadWeapSound(rechamberSoundPlayer);
LoadWeapSound(reloadSound);
LoadWeapSound(reloadSoundPlayer);
LoadWeapSound(reloadEmptySound);
LoadWeapSound(reloadEmptySoundPlayer);
LoadWeapSound(reloadStartSound);
LoadWeapSound(reloadStartSoundPlayer);
LoadWeapSound(reloadEndSound);
LoadWeapSound(reloadEndSoundPlayer);
LoadWeapSound(detonateSound);
LoadWeapSound(detonateSoundPlayer);
LoadWeapSound(nightVisionWearSound);
LoadWeapSound(nightVisionWearSoundPlayer);
LoadWeapSound(nightVisionRemoveSound);
LoadWeapSound(nightVisionRemoveSoundPlayer);
LoadWeapSound(altSwitchSound);
LoadWeapSound(altSwitchSoundPlayer);
LoadWeapSound(raiseSound);
LoadWeapSound(raiseSoundPlayer);
LoadWeapSound(firstRaiseSound);
LoadWeapSound(firstRaiseSoundPlayer);
LoadWeapSound(putawaySound);
LoadWeapSound(putawaySoundPlayer);
LoadWeapSound(scanSound);
if (asset->weapDef->bounceSound)
{
for (size_t i = 0; i < 31; i++)
{
LoadWeapSound(bounceSound[i]);
}
}
LoadWeapSound(projExplosionSound);
LoadWeapSound(projDudSound);
LoadWeapSound(projIgnitionSound);
LoadWeapSound(turretOverheatSound);
LoadWeapSound(turretBarrelSpinMaxSnd);
for (size_t i = 0; i < 4; i++)
{
LoadWeapSound(turretBarrelSpinUpSnd[i]);
LoadWeapSound(turretBarrelSpinDownSnd[i]);
}
LoadWeapSound(missileConeSoundAlias);
LoadWeapSound(missileConeSoundAliasAtBase);
}
void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer)
@ -274,9 +347,9 @@ namespace Assets
{
buffer->align(Utils::Stream::ALIGN_4);
int* ptrs = buffer->dest<int>();
buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*));
buffer->saveMax(31 * sizeof(Game::snd_alias_list_t*));
for (int i = 0; i < 37; i++)
for (int i = 0; i < 31; i++)
{
if (!def->bounceSound[i])
{
@ -433,14 +506,16 @@ namespace Assets
if (def->projExplosionSound)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projExplosionSound->aliasName);
Utils::Stream::ClearPointer(&dest->projExplosionSound);
}
if (def->projDudSound)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projDudSound->aliasName);
Utils::Stream::ClearPointer(&dest->projDudSound);
}
@ -476,7 +551,8 @@ namespace Assets
if (def->projIgnitionSound)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projIgnitionSound->aliasName);
Utils::Stream::ClearPointer(&dest->projIgnitionSound);
}
@ -551,7 +627,8 @@ namespace Assets
if (def->turretOverheatSound)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretOverheatSound->aliasName);
Utils::Stream::ClearPointer(&dest->turretOverheatSound);
}
@ -569,7 +646,8 @@ namespace Assets
if (def->turretBarrelSpinMaxSnd)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd);
}
@ -577,7 +655,8 @@ namespace Assets
for (int i = 0; i < 4; i++) {
if (!def->turretBarrelSpinUpSnd[i]) continue;
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]);
}
@ -585,21 +664,24 @@ namespace Assets
for (int i = 0; i < 4; i++) {
if (!def->turretBarrelSpinDownSnd[i]) continue;
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]);
}
if (def->missileConeSoundAlias)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->missileConeSoundAlias->aliasName);
Utils::Stream::ClearPointer(&dest->missileConeSoundAlias);
}
if (def->missileConeSoundAliasAtBase)
{
buffer->saveMax(4);
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->missileConeSoundAliasAtBase->aliasName);
Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase);
}

View File

@ -1,6 +1,6 @@
#include "StdInclude.hpp"
#define IW4X_CLIPMAP_VERSION 1
#define IW4X_CLIPMAP_VERSION 2
namespace Assets
{
@ -605,7 +605,7 @@ namespace Assets
}
int version = reader.read<int>();
if (version != IW4X_CLIPMAP_VERSION)
if (version > IW4X_CLIPMAP_VERSION)
{
Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version);
}
@ -881,33 +881,38 @@ namespace Assets
clipMap->smodelNodeCount = reader.read<unsigned short>();
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
clipMap->checksum = reader.read<int>();
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts;
// add triggers to mapEnts
/*
std::list<Game::TriggerSlab> slabs;
std::list<Game::TriggerHull> hulls;
std::list<Game::TriggerModel> models;
if (version >= 2) {
if (clipMap->numSubModels > 0) {
clipMap->mapEnts->trigger.count = clipMap->numSubModels;
clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels;
for (int i = 0; i < clipMap->numCModels; ++i)
{
Game::cLeafBrushNode_t* node = &clipMap->cLeafBrushNodes[clipMap->cModels[i].leaf.leafBrushNode];
if (!node->leafBrushCount) continue; // skip empty brushes
Game::TriggerHull* hulls = builder->getAllocator()->allocateArray<Game::TriggerHull>(clipMap->mapEnts->trigger.hullCount);
Game::TriggerModel* models = builder->getAllocator()->allocateArray<Game::TriggerModel>(clipMap->mapEnts->trigger.count);
int baseHull = hulls.size();
for (int j = 0; j < node->leafBrushCount; ++j)
for (unsigned int i = 0; i < clipMap->numSubModels; ++i)
{
Game::cbrush_t* brush = &clipMap->cBrushes[node->data.leaf.brushes[j]];
int baseSlab = slabs.size();
for (int k = 0; k < brush->numsides; ++k)
{
Game::TriggerSlab curSlab;
models[i] = reader.read<Game::TriggerModel>();
hulls[i] = reader.read<Game::TriggerHull>();
}
size_t slabCount = reader.read<size_t>();
clipMap->mapEnts->trigger.slabCount = slabCount;
Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray<Game::TriggerSlab>(clipMap->mapEnts->trigger.slabCount);
for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) {
slabs[i] = reader.read<Game::TriggerSlab>();
}
clipMap->mapEnts->trigger.models = &models[0];
clipMap->mapEnts->trigger.hulls = &hulls[0];
clipMap->mapEnts->trigger.slabs = &slabs[0];
}
}
}
*/
clipMap->checksum = reader.read<int>();
// This mustn't be null and has to have at least 1 'valid' entry.
if (!clipMap->smodelNodeCount || !clipMap->smodelNodes)

View File

@ -4,10 +4,10 @@ namespace Assets
{
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.data()));
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.c_str()));
if (!aliasFile.exists())
{
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound;
return;
}
@ -18,12 +18,34 @@ namespace Assets
return;
}
aliasList->head = builder->getAllocator()->allocate<Game::snd_alias_t>();
std::string errors;
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
json11::Json aliasesContainer = infoData["head"];
auto aliases = aliasesContainer.array_items();
aliasList->count = aliases.size();
// Allocate
aliasList->head = builder->getAllocator()->allocateArray<Game::snd_alias_t>(aliasList->count);
if (!aliasList->head)
{
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
return;
}
aliasList->aliasName = builder->getAllocator()->duplicateString(name.c_str());
for (size_t i = 0; i < aliasList->count; i++)
{
json11::Json head = aliasesContainer[i];
if (!infoData.is_object())
{
Components::Logger::Error("Failed to load sound %s!", name.c_str());
return;
}
aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
if (!aliasList->head->soundFile)
{
@ -31,74 +53,176 @@ namespace Assets
return;
}
aliasList->count = 1;
std::string errors;
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
if (!infoData.is_object())
{
Components::Logger::Error("Failed to load sound %s!", name.data());
return;
}
Game::snd_alias_t* alias = aliasList->head;
// try and parse everything and if it fails then fail for the whole file
auto type = infoData["type"];
auto subtitle = infoData["subtitle"];
auto secondaryAliasName = infoData["secondaryAliasName"];
auto chainAliasName = infoData["chainAliasName"];
auto soundFile = infoData["soundFile"];
auto sequence = infoData["sequence"];
auto volMin = infoData["volMin"];
auto volMax = infoData["volMax"];
auto pitchMin = infoData["pitchMin"];
auto pitchMax = infoData["pitchMax"];
auto distMin = infoData["distMin"];
auto distMax = infoData["distMax"];
auto flags = infoData["flags"];
auto slavePercentage = infoData["slavePercentage"];
auto probability = infoData["probability"];
auto lfePercentage = infoData["lfePercentage"];
auto centerPercentage = infoData["centerPercentage"];
auto startDelay = infoData["startDelay"];
auto volumeFalloffCurve = infoData["volumeFalloffCurve"];
auto envelopMin = infoData["envelopMin"];
auto envelopMax = infoData["envelopMax"];
auto envelopPercentage = infoData["envelopPercentage"];
auto speakerMap = infoData["speakerMap"];
auto type = head["type"];
auto subtitle = head["subtitle"];
auto secondaryAliasName = head["secondaryAliasName"];
auto chainAliasName = head["chainAliasName"];
auto soundFile = head["soundFile"];
auto sequence = head["sequence"];
auto volMin = head["volMin"];
auto volMax = head["volMax"];
auto pitchMin = head["pitchMin"];
auto pitchMax = head["pitchMax"];
auto distMin = head["distMin"];
auto distMax = head["distMax"];
auto flags = head["flags"];
auto slavePercentage = head["slavePercentage"];
auto probability = head["probability"];
auto lfePercentage = head["lfePercentage"];
auto centerPercentage = head["centerPercentage"];
auto startDelay = head["startDelay"];
auto volumeFalloffCurve = head["volumeFalloffCurve"];
auto envelopMin = head["envelopMin"];
auto envelopMax = head["envelopMax"];
auto envelopPercentage = head["envelopPercentage"];
auto speakerMap = head["speakerMap"];
auto aliasName = head["aliasName"];
// Fix casing
if (soundFile.is_null())
{
soundFile = head["soundfile"];
Components::Logger::Print("Fixed casing on %s\n", name.c_str());
}
if (type.is_null() || soundFile.is_null())
{
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile", name.data());
Components::Logger::Print("Type is %s\n", type.dump().c_str());
Components::Logger::Print("SoundFile is %s\n", soundFile.dump().c_str());
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.c_str());
return;
}
#define CHECK(x, type) (x.is_##type##() || x.is_null())
// TODO: actually support all of those properties
if (CHECK(type, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
if (!CHECK(type, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "type", type.type(), type.dump().c_str());
}
if (!CHECK(subtitle, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().c_str());
}
if (!CHECK(aliasName, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "aliasName", aliasName.type(), aliasName.dump().c_str());
}
if (!CHECK(secondaryAliasName, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str());
}
if (!CHECK(chainAliasName, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().c_str());
}
if (!CHECK(soundFile, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().c_str());
}
if (!CHECK(sequence, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().c_str());
}
if (!CHECK(volMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().c_str());
}
if (!CHECK(volMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().c_str());
}
if (!CHECK(pitchMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().c_str());
}
if (!CHECK(pitchMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().c_str());
}
if (!CHECK(probability, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().c_str());
}
if (!CHECK(lfePercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().c_str());
}
if (!CHECK(centerPercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().c_str());
}
if (!CHECK(startDelay, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().c_str());
}
if (!CHECK(volumeFalloffCurve, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().c_str());
}
if (!CHECK(envelopMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().c_str());
}
if (!CHECK(envelopMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().c_str());
}
if (!CHECK(envelopPercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().c_str());
}
if (!CHECK(speakerMap, object))
{
Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().c_str());
}
if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
CHECK(speakerMap, string))
CHECK(speakerMap, object))
{
alias->soundFile->exists = true;
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.string_value().c_str());
if (subtitle.is_string())
{
alias->subtitle = subtitle.string_value().data();
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.string_value().c_str());
}
if (secondaryAliasName.is_string())
{
alias->secondaryAliasName = secondaryAliasName.string_value().data();
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.string_value().c_str());
}
if (chainAliasName.is_string())
{
alias->chainAliasName = chainAliasName.string_value().data();
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.string_value().c_str());
}
alias->sequence = sequence.int_value();
@ -118,35 +242,107 @@ namespace Assets
alias->envelopMax = float(envelopMax.number_value());
alias->envelopPercentage = float(envelopPercentage.number_value());
if (volumeFalloffCurve.is_string())
// Speaker map object
if (!speakerMap.is_null())
{
alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve;
}
if (type.string_value() == "loaded"s)
alias->speakerMap = builder->getAllocator()->allocate<Game::SpeakerMap>();
if (!alias->speakerMap)
{
alias->soundFile->type = Game::SAT_LOADED;
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd;
}
else if (type.string_value() == "streamed"s)
{
alias->soundFile->type = Game::SAT_STREAMED;
std::string streamedFile = soundFile.string_value();
int split = streamedFile.find_last_of('/');
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str());
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str());
}
else
{
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s", name.data(), type.string_value().c_str());
}
}
else
{
Components::Logger::Error("Failed to parse sound %s!", name.data());
Components::Logger::Print("Error allocating memory for speakermap in sound alias%s!\n", alias->aliasName);
return;
}
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].string_value().c_str());
alias->speakerMap->isDefault = speakerMap["isDefault"].bool_value();
if (speakerMap["channelMaps"].is_array())
{
json11::Json::array channelMaps = speakerMap["channelMaps"].array_items();
assert(channelMaps.size() <= 4);
// channelMapIndex should never exceed 1
for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++)
{
// subChannelIndex should never exceed 1
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
{
json11::Json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
auto speakers = channelMap["speakers"].array_items();
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
{
auto speaker = speakers[speakerIndex];
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = static_cast<float>(speaker["levels0"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast<float>(speaker["levels1"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast<int>(speaker["numLevels"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast<int>(speaker["speaker"].number_value());
}
}
}
}
}
if (volumeFalloffCurve.is_string())
{
std::string fallOffCurve = volumeFalloffCurve.string_value();
if (fallOffCurve.size() == 0)
{
fallOffCurve = "$default";
}
auto curve = Components::AssetHandler::FindAssetForZone(
Game::XAssetType::ASSET_TYPE_SOUND_CURVE,
fallOffCurve.c_str(),
builder
).sndCurve;
alias->volumeFalloffCurve = curve;
}
if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
{
alias->soundFile->type = Game::SAT_LOADED;
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd;
}
else if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
{
alias->soundFile->type = Game::SAT_STREAMED;
std::string streamedFile = soundFile.string_value();
std::string directory = ""s;
int split = streamedFile.find_last_of('/');
if (split >= 0)
{
directory = streamedFile.substr(0, split);
streamedFile = streamedFile.substr(split+1);
}
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str());
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str());
}
else
{
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.c_str(), type.string_value().c_str());
return;
}
aliasList->head[i] = *alias;
}
else
{
Components::Logger::Error("Failed to parse sound %s!\n", name.c_str());
return;
}
}
header->sound = aliasList;
#undef CHECK
}
@ -155,7 +351,7 @@ namespace Assets
{
Game::snd_alias_list_t* asset = header.sound;
for (int i = 0; i < asset->count; ++i)
for (unsigned int i = 0; i < asset->count; ++i)
{
Game::snd_alias_t* alias = &asset->head[i];
@ -166,10 +362,15 @@ namespace Assets
if (alias->volumeFalloffCurve)
{
if (!builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve))
{
// (Should never happen, but just in case)
alias->volumeFalloffCurve->filename = "$default";
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve);
}
}
}
}
void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
@ -204,7 +405,7 @@ namespace Assets
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
buffer->saveArray(asset->head, asset->count);
for (int i = 0; i < asset->count; ++i)
for (unsigned int i = 0; i < asset->count; ++i)
{
Game::snd_alias_t* destAlias = &destHead[i];
Game::snd_alias_t* alias = &asset->head[i];

View File

@ -225,6 +225,9 @@ namespace Components
// set flags of cg_drawFPS to archive
Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED);
// un-cheat camera_thirdPersonCrosshairOffset and add archive flags
Utils::Hook::Xor<BYTE>(0x447B41, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
// un-cheat cg_fov and add archive flags
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);

View File

@ -170,10 +170,10 @@ namespace Components
"/dev/full",
"/dev/sdb",
"/dev/sr0",
"/dev//dev/tty0",
"/dev/tty0",
"/dev/urandom",
"Snake",
"lsb_release -a",
"lsb_release -a"
};
static const char* contributors[] =
@ -192,6 +192,7 @@ namespace Components
"INeedGames",
"Killera",
"Lithium",
"Louvenarde",
"OneFourOne",
"quaK",
"RaidMax",

View File

@ -112,6 +112,9 @@ namespace Components
const unsigned int vertOffset = surface->tris.firstVertex + 1;
const unsigned int indexOffset = surface->tris.baseIndex;
// Fuck cube maps for now
if(this->findImage(surface->material, "colorMap")->mapType == 5) continue;
auto& f = this->getFaceList(surface->material);
for (unsigned short j = 0; j < surface->tris.triCount; ++j)
@ -332,33 +335,31 @@ namespace Components
this->object_.append("\n");
}
void writeMaterial(Game::Material* material)
Game::GfxImage* findImage(Game::Material* material, const std::string& type) const
{
std::string name = material->info.name;
Game::GfxImage* image = nullptr;
const auto pos = name.find_last_of('/');
if (pos != std::string::npos)
{
name = name.substr(pos + 1);
}
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
this->object_.append("s off\n");
Game::GfxImage *image = nullptr;
const auto hash = Game::R_HashString(type.data());
for (char l = 0; l < material->textureCount; ++l)
{
if (material->textureTable[l].nameStart == 'c' && material->textureTable[l].nameEnd == 'p')
if (material->textureTable[l].nameHash == hash)
{
image = material->textureTable[l].u.image; // Hopefully our colorMap
image = material->textureTable[l].u.image; // Hopefully our map
break;
}
}
return image;
}
Game::GfxImage* extractImage(Game::Material* material, const std::string& type) const
{
auto* image = this->findImage(material, type);
if (!image)
{
Logger::Print("Failed to get color map for material: %s\n", material->info.name);
return;
return image;
}
// TODO: This is still wrong.
@ -383,12 +384,42 @@ namespace Components
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
}
return image;
}
void writeMaterial(Game::Material* material)
{
std::string name = material->info.name;
const auto pos = name.find_last_of('/');
if (pos != std::string::npos)
{
name = name.substr(pos + 1);
}
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
this->object_.append("s off\n");
auto* colorMap = this->extractImage(material, "colorMap");
auto* normalMap = this->extractImage(material, "normalMap");
auto* specularMap = this->extractImage(material, "specularMap");
this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
this->material_.append("Ka 1.0000 1.0000 1.0000\n");
this->material_.append("Kd 1.0000 1.0000 1.0000\n");
this->material_.append("illum 1\n");
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", colorMap->name));
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", colorMap->name));
if (specularMap)
{
this->material_.append(Utils::String::VA("map_Ks textures/%s.png\n", specularMap->name));
}
if (normalMap)
{
this->material_.append(Utils::String::VA("bump textures/%s.png\n", normalMap->name));
}
}
void writeFaces()

View File

@ -740,6 +740,22 @@ namespace Components
Utils::Hook::Call<void(Game::gentity_s*, int, int)>(0x408910)(ent, unk, unk2);
}
bool Maps::SV_SetTriggerModelHook(Game::gentity_s* ent) {
// Use me for debugging
//std::string classname = Game::SL_ConvertToString(ent->script_classname);
//std::string targetname = Game::SL_ConvertToString(ent->targetname);
return Utils::Hook::Call<bool(Game::gentity_s*)>(0x5050C0)(ent);
}
int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) {
#ifdef DEBUG
Game::MapEnts* ents = *reinterpret_cast<Game::MapEnts**>(0x1AA651C); // Use me for debugging
#endif
return Utils::Hook::Call<int16(int, Game::Bounds*)>(0x4416C0)(modelPointer, bounds);
}
Maps::Maps()
{
Dvar::OnInit([]()
@ -779,6 +795,15 @@ namespace Components
Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
#ifdef DEBUG
// Check trigger models
Utils::Hook(0x5FC0F1, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick();
Utils::Hook(0x5FC2671, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick();
Utils::Hook(0x5050D4, Maps::CM_TriggerModelBounds, HOOK_CALL).install()->quick();
#endif
//
//#define SORT_SMODELS
#if !defined(DEBUG) || !defined(SORT_SMODELS)
// Don't sort static models

View File

@ -120,5 +120,7 @@ namespace Components
static void SetSpecularStub1();
static void SetSpecularStub2();
static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2);
static bool SV_SetTriggerModelHook(Game::gentity_s* ent);
static int16 CM_TriggerModelBounds(int brushModelPointer, Game::Bounds* bounds);
};
}

View File

@ -946,11 +946,6 @@ namespace Components
}
});
Dvar::OnInit([]
{
Dvar::Register<bool>("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees");
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get<bool>()) return;
@ -973,6 +968,181 @@ namespace Components
}
});
Dvar::OnInit([]
{
Dvar::Register<bool>("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes");
Dvar::Register<bool>("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions");
Dvar::Register<bool>("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers");
Dvar::Register<bool>("r_drawModelNames", false, Game::DVAR_FLAG_CHEAT, "Draw all model names");
Dvar::Register<bool>("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees");
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawModelNames").get<bool>()) return;
float sceneModelsColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
float dobjsColor[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float staticModelsColor[4] = { 1.0f, 0.0f, 1.0f, 1.0f };
auto mapName = Dvar::Var("mapname").get<const char*>();
auto* scene = Game::scene;
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
}
for (auto i = 0; i < scene->sceneDObjCount; i++)
{
if (scene->sceneDObj[i].obj) {
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
{
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
}
}
}
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
if (staticModel.model) {
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get<bool>()) return;
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float blue[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
auto* scene = Game::scene;
for(auto i = 0; i < scene->sceneModelCount; i++)
{
if(!scene->sceneModel[i].model)
continue;
auto b = scene->sceneModel[i].model->bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(red, &b, &scene->sceneModel[i].placement.base.quat);
}
for(auto i = 0; i < scene->sceneDObjCount; i++)
{
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
scene->sceneDObj[i].cull.bounds.halfSize[2] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[2]);
if (scene->sceneDObj[i].cull.bounds.halfSize[0] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[1] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[2] < 0) {
Components::Logger::Print("WARNING: Negative half size for DOBJ %s, this will cause culling issues!", scene->sceneDObj[i].obj->models[0]->name);
}
Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds);
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized()) return;
if (!Dvar::Var("r_drawSceneModelCollisions").get<bool>()) return;
float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* scene = Game::scene;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
for (auto j = 0; j < scene->sceneModel[i].model->numCollSurfs; j++) {
auto b = scene->sceneModel[i].model->collSurfs[j].bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(green, &b, &scene->sceneModel[i].placement.base.quat);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawTriggers").get<bool>()) return;
float hurt[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float hurtTouch[4] = { 0.75f, 0.0f, 0.0f, 1.0f };
float damage[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* entities = Game::g_entities;
for(auto i = 0u; i < 0x800; i++)
{
auto* ent = &entities[i];
if(ent->r.isInUse)
{
Game::Bounds b = ent->r.box;
b.midPoint[0] += ent->r.currentOrigin[0];
b.midPoint[1] += ent->r.currentOrigin[1];
b.midPoint[2] += ent->r.currentOrigin[2];
switch(ent->handler)
{
case Game::ENT_HANDLER_TRIGGER_HURT:
Game::R_AddDebugBounds(hurt, &b);
break;
case Game::ENT_HANDLER_TRIGGER_HURT_TOUCH:
Game::R_AddDebugBounds(hurtTouch, &b);
break;
case Game::ENT_HANDLER_TRIGGER_DAMAGE:
Game::R_AddDebugBounds(damage, &b);
break;
case Game::ENT_HANDLER_TRIGGER_MULTIPLE:
if(ent->spawnflags & 0x40)
Game::R_AddDebugBounds(once, &b);
else
Game::R_AddDebugBounds(multiple, &b);
break;
default:
float rv = std::min((float)ent->handler, (float)5) / 5;
float gv = std::clamp((float)ent->handler-5, (float)0, (float)5) / 5;
float bv = std::clamp((float)ent->handler - 10, (float)0, (float)5) / 5;
float color[4] = { rv, gv, bv, 1.0f };
Game::R_AddDebugBounds(color, &b);
break;
}
}
}
});
// Dvars
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");

View File

@ -108,7 +108,7 @@ namespace Components
void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state)
{
Game::Com_Error(0, "Tried to use '%s' when it isn't valid for material '%s' and technique '%s'",
Game::Com_Error(0, "Tried to use sampler '%s' when it isn't valid for material '%s' and technique '%s'",
sampler, state->material->info.name, state->technique->name);
}

View File

@ -409,7 +409,7 @@ namespace Components
{
Logger::Error(5, "Entity: %i is not a client", gentity);
}
return &Game::svs_clients[gentity->number];
return &Game::svs_clients[gentity->s.number];
}
void Script::AddFunctions()

View File

@ -44,7 +44,7 @@ namespace Components
if (!found)
{
Logger::Error("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type));
Logger::Print("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type));
}
}
@ -222,8 +222,10 @@ namespace Components
}
Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset);
if (!assetHeader.data)
{ Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type));
{
Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type));
return false;
}

View File

@ -1655,18 +1655,48 @@ namespace Components
image->delayLoadPixels = image359.loaded;
image->name = image359.name;
FixImageCategory(image);
// Used for later stuff
(&image->delayLoadPixels)[1] = image359.pad3[1];
}
else
{
std::memcpy(buffer + 28, buffer + (size - 4), 4);
Game::GfxImage* image = reinterpret_cast<Game::GfxImage*>(buffer);
FixImageCategory(image);
}
}
return result;
}
void Zones::FixImageCategory(Game::GfxImage* image) {
// CODO makes use of additional enumerator values (9, 10, 11) that don't exist in IW4
// We have to translate them. 9 is for Reflection probes, 11 is for Compass, 10 is for Lightmap
switch (image->category)
{
case 9:
image->category = Game::ImageCategory::IMG_CATEGORY_AUTO_GENERATED;
break;
case 10:
image->category = Game::ImageCategory::IMG_CATEGORY_LIGHTMAP;
break;
case 11:
image->category = Game::ImageCategory::IMG_CATEGORY_LOAD_FROM_FILE;
break;
}
if (image->category > 7 || image->category < 0) {
#ifdef DEBUG
if (IsDebuggerPresent()) __debugbreak();
#endif
}
}
bool Zones::LoadXAsset(bool atStreamStart, char* buffer, int size)
{
int count = 0;

View File

@ -100,5 +100,6 @@ namespace Components
static void LoadMaterialAsset(Game::Material** asset);
static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size);
static void LoadTracerDefFxEffect();
static void FixImageCategory(Game::GfxImage* image);
};
}

View File

@ -425,6 +425,8 @@ namespace Game
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
{
int elSize = DB_GetXAssetSizeHandlers[type]();
@ -734,6 +736,29 @@ namespace Game
std::memmove(&solution[0], &res[0], sizeof(res));
}
void QuatRot(vec3_t* vec, const vec4_t* quat)
{
vec4_t q{ (*quat)[3],(*quat)[0],(*quat)[1],(*quat)[2] };
vec4_t res{ 0, (*vec)[0], (*vec)[1], (*vec)[2] };
vec4_t res2;
vec4_t quat_conj{ q[0], -q[1], -q[2], -q[3] };
QuatMultiply(&q, &res, &res2);
QuatMultiply(&res2, &quat_conj, &res);
(*vec)[0] = res[1];
(*vec)[1] = res[2];
(*vec)[2] = res[3];
}
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res)
{
(*res)[0] = (*q2)[0] * (*q1)[0] - (*q2)[1] * (*q1)[1] - (*q2)[2] * (*q1)[2] - (*q2)[3] * (*q1)[3];
(*res)[1] = (*q2)[0] * (*q1)[1] + (*q2)[1] * (*q1)[0] - (*q2)[2] * (*q1)[3] + (*q2)[3] * (*q1)[2];
(*res)[2] = (*q2)[0] * (*q1)[2] + (*q2)[1] * (*q1)[3] + (*q2)[2] * (*q1)[0] - (*q2)[3] * (*q1)[1];
(*res)[3] = (*q2)[0] * (*q1)[3] - (*q2)[1] * (*q1)[2] + (*q2)[2] * (*q1)[1] + (*q2)[3] * (*q1)[0];
}
void SortWorldSurfaces(GfxWorld* world)
{
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
@ -812,6 +837,73 @@ namespace Game
Game::R_AddDebugLine(color, v4, v8);
}
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4])
{
vec3_t v[8];
auto* center = b->midPoint;
auto* halfSize = b->halfSize;
v[0][0] = -halfSize[0];
v[0][1] = -halfSize[1];
v[0][2] = -halfSize[2];
v[1][0] = halfSize[0];
v[1][1] = -halfSize[1];
v[1][2] = -halfSize[2];
v[2][0] = -halfSize[0];
v[2][1] = halfSize[1];
v[2][2] = -halfSize[2];
v[3][0] = halfSize[0];
v[3][1] = halfSize[1];
v[3][2] = -halfSize[2];
v[4][0] = -halfSize[0];
v[4][1] = -halfSize[1];
v[4][2] = halfSize[2];
v[5][0] = halfSize[0];
v[5][1] = -halfSize[1];
v[5][2] = halfSize[2];
v[6][0] = -halfSize[0];
v[6][1] = halfSize[1];
v[6][2] = halfSize[2];
v[7][0] = halfSize[0];
v[7][1] = halfSize[1];
v[7][2] = halfSize[2];
for(auto& vec : v)
{
QuatRot(&vec, quat);
vec[0] += center[0];
vec[1] += center[1];
vec[2] += center[2];
}
// bottom
Game::R_AddDebugLine(color, v[0], v[1]);
Game::R_AddDebugLine(color, v[1], v[3]);
Game::R_AddDebugLine(color, v[3], v[2]);
Game::R_AddDebugLine(color, v[2], v[0]);
// top
Game::R_AddDebugLine(color, v[4], v[5]);
Game::R_AddDebugLine(color, v[5], v[7]);
Game::R_AddDebugLine(color, v[7], v[6]);
Game::R_AddDebugLine(color, v[6], v[4]);
// verticals
Game::R_AddDebugLine(color, v[0], v[4]);
Game::R_AddDebugLine(color, v[1], v[5]);
Game::R_AddDebugLine(color, v[2], v[6]);
Game::R_AddDebugLine(color, v[3], v[7]);
}
#pragma optimize("", off)
__declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/)
{

View File

@ -870,6 +870,8 @@ namespace Game
extern clientstate_t* clcState;
extern GfxScene* scene;
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
void Menu_FreeItemMemory(Game::itemDef_s* item);
const char* TableLookup(StringTable* stringtable, int row, int column);
@ -918,9 +920,12 @@ namespace Game
void Vec3Normalize(vec3_t& vec);
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
void QuatRot(vec3_t* vec, const vec4_t* quat);
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res);
void SortWorldSurfaces(GfxWorld* world);
void R_AddDebugLine(float* color, float* v1, float* v2);
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
void R_AddDebugBounds(float* color, Bounds* b);
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4]);
}

View File

@ -98,6 +98,19 @@ namespace Game
DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar
} dvar_flag;
enum ImageCategory : char
{
IMG_CATEGORY_UNKNOWN = 0x0,
IMG_CATEGORY_AUTO_GENERATED = 0x1,
IMG_CATEGORY_LIGHTMAP = 0x2,
IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
IMG_CATEGORY_RAW = 0x4,
IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
IMG_CATEGORY_WATER = 0x5,
IMG_CATEGORY_RENDERTARGET = 0x6,
IMG_CATEGORY_TEMP = 0x7,
} ;
enum DvarSetSource
{
DVAR_SOURCE_INTERNAL = 0x0,
@ -893,7 +906,7 @@ namespace Game
struct MSSChannelMap
{
int speakerCount;
unsigned int speakerCount;
MSSSpeakerLevels speakers[6];
};
@ -937,7 +950,7 @@ namespace Game
{
const char *aliasName;
snd_alias_t *head;
int count;
unsigned int count;
};
struct cStaticModel_s
@ -4461,18 +4474,148 @@ namespace Game
char pad3[724];
} gclient_t;
typedef struct gentity_s
struct LerpEntityState
{
char pad[0x70];
};
struct clientLinkInfo_t
{
__int16 parentId;
char tagName;
char flags;
};
struct entityState_s
{
int number;
unsigned char pad[308]; // 4
float origin[3]; // 312
float angles[3]; // 324
char pad2[8];
int eType;
LerpEntityState lerp;
int time2;
int otherEntityNum;
int attackerEntityNum;
int groundEntityNum;
int loopSound;
int surfType;
union
{
int brushModel;
int triggerModel;
int item;
int xmodel;
int primaryLight;
} index;
int clientNum;
int iHeadIcon;
int iHeadIconTeam;
int solid;
unsigned int eventParm;
int eventSequence;
int events[4];
unsigned int eventParms[4];
unsigned __int16 weapon;
int legsAnim;
int torsoAnim;
int un1;
int un2;
clientLinkInfo_t clientLinkInfo;
unsigned int partBits[6];
int clientMask[1];
};
struct EntHandle
{
unsigned __int16 number;
unsigned __int16 infoIndex;
};
struct entityShared_t
{
char isLinked;
char modelType;
char svFlags;
char isInUse;
Bounds box;
int contents;
Bounds absBox;
float currentOrigin[3];
float currentAngles[3];
EntHandle ownerNum;
int eventTime;
};
enum EntHandler
{
ENT_HANDLER_NULL = 0x0,
ENT_HANDLER_TRIGGER_MULTIPLE = 0x1,
ENT_HANDLER_TRIGGER_HURT = 0x2,
ENT_HANDLER_TRIGGER_HURT_TOUCH = 0x3,
ENT_HANDLER_TRIGGER_DAMAGE = 0x4,
ENT_HANDLER_SCRIPT_MOVER = 0x5,
ENT_HANDLER_SCRIPT_MODEL = 0x6,
ENT_HANDLER_GRENADE = 0x7,
ENT_HANDLER_TIMED_OBJECT = 0x8,
ENT_HANDLER_ROCKET = 0x9,
ENT_HANDLER_CLIENT = 0xA,
ENT_HANDLER_CLIENT_SPECTATOR = 0xB,
ENT_HANDLER_CLIENT_DEAD = 0xC,
ENT_HANDLER_PLAYER_CLONE = 0xD,
ENT_HANDLER_TURRET_INIT = 0xE,
ENT_HANDLER_TURRET = 0xF,
ENT_HANDLER_DROPPED_ITEM = 0x10,
ENT_HANDLER_ITEM_INIT = 0x11,
ENT_HANDLER_ITEM = 0x12,
ENT_HANDLER_PRIMARY_LIGHT = 0x13,
ENT_HANDLER_PLAYER_BLOCK = 0x14,
ENT_HANDLER_VEHICLE = 0x15,
ENT_HANDLER_COUNT
};
typedef struct gentity_s
{
entityState_s s;
entityShared_t r;
gclient_t* client; // 344
unsigned char pad3[28];
void /*Turret*/* turret;
void /*Vehicle*/* vehicle;
int physObjId;
unsigned __int16 model;
char physicsObject;
char takedamage;
char active;
char handler;
char team;
bool freeAfterEvent;
__int16 padding_short;
short classname;
short pad4;
unsigned char pad5[248];
unsigned __int16 script_classname;
unsigned __int16 script_linkName;
unsigned __int16 target;
unsigned __int16 targetname;
unsigned int attachIgnoreCollision;
int spawnflags;
int flags;
int eventTime;
int clipmask;
int processedFrame;
EntHandle parent;
int nextthink;
int health;
int maxHealth;
int damage;
int count;
EntHandle missileTargetEnt;
EntHandle remoteControlledOwner;
gentity_s* tagChildren;
unsigned __int16 attachModelNames[19];
unsigned __int16 attachTagNames[19];
int useCount;
gentity_s* nextFree;
int birthTime;
char pad[100];
} gentity_t;
#pragma pack(push, 1)
@ -4816,6 +4959,310 @@ namespace Game
GfxCmdBufState *state;
};
struct GfxDrawGroupSetupFields
{
unsigned __int16 materialSortedIndex : 15;
unsigned __int16 useHeroLighting : 1;
char sceneLightIndex;
char surfType;
};
union GfxDrawGroupSetup
{
GfxDrawGroupSetupFields fields;
unsigned int packed;
};
struct GfxMarkSurfLightingFields
{
char lmapIndex;
char reflectionProbeIndex;
unsigned __int16 modelIndex;
};
union GfxMarkSurfLighting
{
GfxMarkSurfLightingFields fields;
unsigned int packed;
};
struct GfxMarkSurf
{
GfxDrawGroupSetup drawGroup;
unsigned __int16* indices;
unsigned __int16 triCount;
char modelType;
char pad;
GfxMarkSurfLighting lighting;
};
struct GfxCodeSurf
{
GfxDrawGroupSetup drawGroup;
unsigned int triCount;
unsigned __int16* indices;
unsigned __int16 argOffset;
unsigned __int16 argCount;
};
struct __declspec(align(4)) GfxGlassSurf
{
GfxDrawGroupSetup drawGroup;
char pad;
char reflectionProbeIndex;
unsigned __int16 triCount;
unsigned __int16* indices;
unsigned __int16 lightingHandle;
};
struct GfxCloudSurfFields
{
unsigned __int16 materialSortedIndex;
char cloudDataIndex;
char surfType;
};
union GfxCloudSurf
{
GfxCloudSurfFields fields;
unsigned int packed;
};
struct GfxSparkSurfFields
{
unsigned __int16 materialSortedIndex;
unsigned __int16 sparkDataIndex;
};
union GfxSparkSurf
{
GfxSparkSurfFields fields;
unsigned int packed;
};
struct GfxSceneDef
{
int time;
float floatTime;
float viewOffset[3];
GfxImage* sunShadowImage;
float sunShadowPixelAdjust[4];
};
struct GfxLight
{
char type;
char canUseShadowMap;
char unused[2];
float color[3];
float dir[3];
float origin[3];
float radius;
float cosHalfFovOuter;
float cosHalfFovInner;
int exponent;
unsigned int spotShadowIndex;
GfxLightDef* def;
};
struct GfxVisibleLight
{
char pad[0x2004];
};
struct GfxEntity
{
unsigned int renderFxFlags;
float materialTime;
};
struct GfxSkinnedXModelSurfs
{
void* firstSurf;
};
struct GfxSceneEntityCull
{
volatile unsigned int state;
Bounds bounds;
GfxSkinnedXModelSurfs skinnedSurfs;
};
union GfxSceneEntityInfo
{
void/*cpose_t*/* pose;
unsigned __int16* cachedLightingHandle;
};
struct DSkelPartBits
{
int anim[6];
int control[6];
int worldCtrl[6];
int skel[6];
};
struct DSkel
{
DSkelPartBits partBits;
int timeStamp;
/*DObjAnimMat*/void* mat;
};
struct DObj
{
/*XAnimTree_s*/ void* tree;
unsigned __int16 duplicateParts;
unsigned __int16 entnum;
char duplicatePartsSize;
char numModels;
char numBones;
char flags;
unsigned int ignoreCollision;
volatile int locked;
DSkel skel;
float radius;
unsigned int hidePartBits[6];
XModel** models;
};
struct GfxSceneEntity
{
float lightingOrigin[3];
GfxPlacement placement;
GfxSceneEntityCull cull;
char lods[32];
unsigned __int32 gfxEntIndex : 7;
unsigned __int32 entnum : 12;
unsigned __int32 renderFxFlags : 13;
DObj* obj;
GfxSceneEntityInfo info;
char reflectionProbeIndex;
};
struct GfxScaledPlacement
{
GfxPlacement base;
float scale;
};
struct GfxSceneModel
{
XModelDrawInfo info;
XModel* model;
DObj* obj;
GfxScaledPlacement placement;
unsigned __int32 gfxEntIndex : 7;
unsigned __int32 entnum : 12;
unsigned __int32 renderFxFlags : 13;
float radius;
unsigned __int16* cachedLightingHandle;
float lightingOrigin[3];
char reflectionProbeIndex;
char lod;
};
struct __declspec(align(4)) GfxSceneBrush
{
BModelDrawInfo info;
unsigned __int16 entnum;
GfxBrushModel* bmodel;
GfxPlacement placement;
char reflectionProbeIndex;
};
union GfxSceneGlass
{
struct
{
bool rendered;
char reflectionProbeIndex;
unsigned __int16 lightingHandle;
};
unsigned int packed;
};
union GfxEntCellRefInfo
{
float radius;
GfxBrushModel* bmodel;
};
struct GfxSceneDpvs
{
unsigned int localClientNum;
char* entVisData[7];
unsigned __int16* sceneXModelIndex;
unsigned __int16* sceneDObjIndex;
GfxEntCellRefInfo* entInfo[4];
};
struct __declspec(align(64)) GfxScene
{
GfxCodeSurf codeEmissiveSurfs[2048];
GfxCodeSurf codeTransSurfs[640];
GfxMarkSurf markSurfs[1536];
GfxGlassSurf glassSurfs[768];
GfxCloudSurf cloudSurfs[256];
GfxDrawSurf drawSurfsDepthHack[32];
GfxDrawSurf drawSurfsLitOpaque[8192];
GfxDrawSurf drawSurfsLitTrans[2048];
GfxDrawSurf drawSurfsEmissive[8192];
GfxDrawSurf drawSurfsSunShadow0[4096];
GfxDrawSurf drawSurfsSunShadow1[8192];
GfxDrawSurf drawSurfsSpotShadow0[896];
GfxDrawSurf drawSurfsSpotShadow1[896];
GfxDrawSurf drawSurfsSpotShadow2[896];
GfxDrawSurf drawSurfsSpotShadow3[896];
unsigned int sceneLightIsUsed[32];
unsigned int cachedSceneLightIsUsed[4][32];
GfxSparkSurf sparkSurfs[64];
unsigned int drawSurfLimit[10];
volatile int drawSurfCount[10];
GfxDrawSurf* drawSurfs[10];
volatile int codeSurfUser[2];
volatile int markMeshGuard;
unsigned int codeEmissiveSurfCount;
unsigned int codeTransSurfCount;
unsigned int markSurfCount;
unsigned int glassSurfCount;
GfxSceneDef def;
unsigned int addedLightCount;
GfxLight addedLight[32];
bool isAddedLightCulled[32];
float dynamicSpotLightNearPlaneOffset;
float dynamicSpotLightLength;
GfxVisibleLight visLight[4];
GfxVisibleLight visLightShadow[1];
unsigned int* entOverflowedDrawBuf;
volatile int gfxEntCount;
GfxEntity gfxEnts[128];
int sceneDObjCount;
int preClientSceneDObjCount;
int sceneDObjCountAtMark;
GfxSceneEntity sceneDObj[520];
char sceneDObjVisData[7][512];
int sceneDObjMarkableViewmodelIndex;
unsigned int sceneDObjFirstViewmodelIndex;
unsigned int sceneDObjViewmodelCount;
volatile int sceneModelCount;
int sceneModelCountAtMark;
int sceneDObjModelCount;
GfxSceneModel sceneModel[1024];
char sceneModelVisData[7][1024];
volatile int sceneBrushCount;
int sceneBrushCountAtMark;
GfxSceneBrush sceneBrush[512];
char sceneBrushVisData[3][512];
GfxSceneGlass sceneGlass[1024];
unsigned int sceneDynModelCount;
unsigned int sceneDynBrushCount;
int gfxEntCountAtMark;
GfxSceneDpvs dpvs;
int updateSound;
int allowAddDObj;
};
#pragma endregion
#ifndef IDA

View File

@ -5,8 +5,8 @@
#ifndef RC_INVOKED
#define _HAS_CXX17 1
#define _HAS_CXX20 1
//#define _HAS_CXX17 1
//#define _HAS_CXX20 1
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
@ -38,6 +38,7 @@
#include <future>
#include <unordered_map>
#include <queue>
#include <algorithm>
// Experimental C++17 features
#include <filesystem>