Skip to content

Commit

Permalink
Add jumpstat area/ljroom teleport support (#261)
Browse files Browse the repository at this point in the history
* Lj room teleport basics, untested

Windows, please don't delete my files

* Go back to roundIsStarting

* Clang format

* str*cmp considered harmful

* Clang format missed file

* Add gravity to modifier trigger

* Clang format

* Gravity trigger works again

* Rename KZ_STREQ* macros to be less misleading

KZ_STRNEQ could be interpreted as string not equals

* Add back apiVersionLoaded

* Clang format

* Invalidate jumpstat if gravityscale isn't 1

* Fix rebase kerfuffle with hookentities

* Fix touch called with endtouch on mapping api triggers

This would have teleported you twice and would mess up relative teleport triggers

* Add translation for Player gravity scale changed
  • Loading branch information
GameChaos authored Oct 31, 2024
1 parent 88095d8 commit efba178
Show file tree
Hide file tree
Showing 14 changed files with 154 additions and 80 deletions.
3 changes: 2 additions & 1 deletion mapping_api/game/csgo_core/csgo_internal.fgd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
timer_modifier_disable_teleports(boolean) {group="KZ | Modifiers" enabled={variable="timer_trigger_type" value="1"}} : "Disable Teleports" : 0 : "Disable teleporting when the player is touching this trigger"
timer_modifier_disable_jumpstats(boolean) {group="KZ | Modifiers" enabled={variable="timer_trigger_type" value="1"}} : "Disable Jumpstats" : 0 : "Disable jumpstats when the player is touching this trigger"
timer_modifier_enable_slide(boolean) {group="KZ | Modifiers" enabled={variable="timer_trigger_type" value="1"}} : "Enable Slide" : 0 : "Makes every surface surfable, if the player is touching this trigger."
timer_modifier_gravity(float) {group="KZ | Modifiers" enabled={variable="timer_trigger_type" value="1"}} : "Player Gravity" : "1.0" : "Scales the player's gravity by this amount."

// KZ Anti Bhop
timer_anti_bhop_time(float) {group="KZ | Anti Bhop" min="0.0" enabled={variable="timer_trigger_type" value="4"}} : "Time" : "0.2" : "Only works if the Name is timer_anti_bhop. "
Expand Down Expand Up @@ -79,5 +80,5 @@

@OverrideClass = info_teleport_destination
[
timer_zone_course_descriptor(target_destination) : "KZ | Course Descriptor" : : "info_target_server_only entity that describes which course this teleport destination is connected to. Every zone trigger must be associated with a course. Maximum length is 127 characters."
timer_zone_course_descriptor(target_destination) : "KZ | Course Descriptor" : : "info_target_server_only entity that describes which course this teleport destination is connected to."
]
8 changes: 8 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,11 @@ typedef double f64;
// Common macro for service event listeners in different parts of the project.
#define CALL_FORWARD(listeners, func, ...) FOR_EACH_VEC(listeners, i) listeners[i]->func(__VA_ARGS__);
#define CALL_FORWARD_BOOL(retValue, listeners, func, ...) FOR_EACH_VEC(listeners, i) retValue &= listeners[i]->func(__VA_ARGS__)

// str*cmp considered harmful.
// Macros to make sure you don't mess up checking if strings are equal.
// The I means case insensitive.
#define KZ_STREQ(a, b) (V_strcmp(a, b) == 0)
#define KZ_STREQI(a, b) (V_stricmp(a, b) == 0)
#define KZ_STREQLEN(a, b, maxlen) (V_strncmp(a, b, maxlen) == 0)
#define KZ_STREQILEN(a, b, maxlen) (V_strnicmp(a, b, maxlen) == 0)
4 changes: 4 additions & 0 deletions src/kz/jumpstats/kz_jumpstats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,10 @@ void KZJumpstatsService::DetectExternalModifications()
{
this->InvalidateJumpstats("Base velocity detected");
}
if (this->player->GetPlayerPawn()->m_flGravityScale() != 1)
{
this->InvalidateJumpstats("Player gravity scale changed");
}
}

void KZJumpstatsService::DetectWater()
Expand Down
114 changes: 64 additions & 50 deletions src/kz/mappingapi/kz_mappingapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,20 @@ enum

static_global struct
{
CUtlVectorFixed<KzTrigger, 2048> triggers;
CUtlVectorFixed<KZCourseDescriptor, KZ_MAX_COURSE_COUNT> courseDescriptors;
i32 mapApiVersion = KZ_NO_MAPAPI_VERSION;
bool apiVersionLoaded = false;
bool fatalFailure = false;
bool roundIsStarting = true;
i32 mapApiVersion;
bool apiVersionLoaded;
bool fatalFailure;

CUtlVectorFixed<KzTrigger, 2048> triggers;
bool roundIsStarting;
i32 errorFlags;
i32 errorCount;
char errors[32][256];

void OnRoundPrestart()
{
triggers.RemoveAll();
errorFlags = 0;
errorCount = 0;
roundIsStarting = true;
for (auto error : errors)
{
error[0] = 0;
}
}
bool hasJumpstatArea;
Vector jumpstatAreaPos;
QAngle jumpstatAreaAngles;
} g_mappingApi;

static_global CTimer<> *g_errorTimer;
Expand Down Expand Up @@ -119,7 +112,7 @@ static_function bool Mapi_CreateCourse(i32 courseNumber = 1, const char *courseN
// This should only happen during start/end zone backwards compat where hammer IDs are KZ_NO_MAPAPI_VERSION, so this is not an error.
return false;
}
if (!V_stricmp(targetName, currentCourses[i].entityTargetname))
if (KZ_STREQI(targetName, currentCourses[i].entityTargetname))
{
Mapi_Error("Course descriptor '%s' already existed! (registered by Hammer ID %i)", targetName, currentCourses[i].hammerId);
return false;
Expand Down Expand Up @@ -188,8 +181,6 @@ static_function void Mapi_OnTriggerMultipleSpawn(const EntitySpawnInfo_t *info)
if (!g_mappingApi.roundIsStarting)
{
// Only allow triggers and zones that were spawned during the round start phase.
Mapi_Error("Trigger %s spawned after the map was loaded, the trigger won't be loaded! Hammer ID %i, origin (%.0f %.0f %.0f)",
g_triggerNames[type], hammerId, origin.x, origin.y, origin.z);
return;
}

Expand All @@ -215,6 +206,7 @@ static_function void Mapi_OnTriggerMultipleSpawn(const EntitySpawnInfo_t *info)
trigger.modifier.disableTeleports = ekv->GetBool("timer_modifier_disable_teleports");
trigger.modifier.disableJumpstats = ekv->GetBool("timer_modifier_disable_jumpstats");
trigger.modifier.enableSlide = ekv->GetBool("timer_modifier_enable_slide");
trigger.modifier.gravity = ekv->GetFloat("timer_modifier_gravity", 1);
}
break;

Expand Down Expand Up @@ -359,7 +351,7 @@ static_function void Mapi_OnInfoTargetSpawn(const CEntityKeyValues *ekv)
const char *courseName = ekv->GetString("timer_course_name");
const char *targetName = ekv->GetString("targetname");
constexpr static_persist const char *targetNamePrefix = "[PR#]";
if (!V_strncmp(targetName, targetNamePrefix, strlen(targetNamePrefix)))
if (KZ_STREQLEN(targetName, targetNamePrefix, strlen(targetNamePrefix)))
{
targetName = targetName + strlen(targetNamePrefix);
}
Expand Down Expand Up @@ -422,7 +414,7 @@ static_function KZCourseDescriptor *Mapi_FindCourse(const char *targetname)

FOR_EACH_VEC(g_mappingApi.courseDescriptors, i)
{
if (V_stricmp(g_mappingApi.courseDescriptors[i].entityTargetname, targetname) == 0)
if (KZ_STREQI(g_mappingApi.courseDescriptors[i].entityTargetname, targetname))
{
result = &g_mappingApi.courseDescriptors[i];
break;
Expand All @@ -448,27 +440,32 @@ static_function void Mapi_OnInfoTeleportDestinationSpawn(const EntitySpawnInfo_t
{
const CEntityKeyValues *ekv = info->m_pKeyValues;
const char *targetname = ekv->GetString("targetname");
if (V_stricmp(targetname, "timer_start"))
if (KZ_STREQI(targetname, "timer_start"))
{
return;
}
if (g_mappingApi.mapApiVersion == KZ_NO_MAPAPI_VERSION)
{
Mapi_SetStartPosition(KZ_NO_MAPAPI_COURSE_DESCRIPTOR, ekv->GetVector("origin"), ekv->GetQAngle("angles"));
}
else if (g_mappingApi.mapApiVersion == KZ_MAPAPI_VERSION) // TODO do better check
{
const char *courseDescriptor = ekv->GetString("timer_zone_course_descriptor");
i32 hammerId = ekv->GetInt("hammerUniqueId", -1);
Vector origin = ekv->GetVector("origin");
if (!courseDescriptor || !courseDescriptor[0])
if (g_mappingApi.mapApiVersion == KZ_NO_MAPAPI_VERSION)
{
Mapi_Error("Course descriptor targetname of timer_start info_teleport_destination is empty! Hammer ID %i, origin (%.0f %.0f %.0f)",
hammerId, origin.x, origin.y, origin.z);
assert(0);
return;
Mapi_SetStartPosition(KZ_NO_MAPAPI_COURSE_DESCRIPTOR, ekv->GetVector("origin"), ekv->GetQAngle("angles"));
}
else if (g_mappingApi.mapApiVersion == KZ_MAPAPI_VERSION) // TODO do better check
{
const char *courseDescriptor = ekv->GetString("timer_zone_course_descriptor");
i32 hammerId = ekv->GetInt("hammerUniqueId", -1);
Vector origin = ekv->GetVector("origin");
if (!courseDescriptor || !courseDescriptor[0])
{
Mapi_Error("Course descriptor targetname of timer_start info_teleport_destination is empty! Hammer ID %i, origin (%.0f %.0f %.0f)",
hammerId, origin.x, origin.y, origin.z);
assert(0);
return;
}
Mapi_SetStartPosition(courseDescriptor, origin, ekv->GetQAngle("angles"));
}
Mapi_SetStartPosition(courseDescriptor, origin, ekv->GetQAngle("angles"));
}
else if (KZ_STREQI(targetname, "timer_jumpstat_area"))
{
g_mappingApi.hasJumpstatArea = true;
g_mappingApi.jumpstatAreaPos = ekv->GetVector("origin");
g_mappingApi.jumpstatAreaAngles = ekv->GetQAngle("angles");
}
};

Expand All @@ -485,8 +482,13 @@ void KZ::mapapi::OnCreateLoadingSpawnGroupHook(const CUtlVector<const CEntityKey
{
return;
}
g_mappingApi.fatalFailure = false;
for (i32 i = 0; i < pKeyValues->Count() && !g_mappingApi.apiVersionLoaded; i++)

if (g_mappingApi.apiVersionLoaded)
{
return;
}

for (i32 i = 0; i < pKeyValues->Count(); i++)
{
auto ekv = (*pKeyValues)[i];

Expand All @@ -495,7 +497,7 @@ void KZ::mapapi::OnCreateLoadingSpawnGroupHook(const CUtlVector<const CEntityKey
continue;
}
const char *classname = ekv->GetString("classname");
if (V_stricmp(classname, "worldspawn") == 0)
if (KZ_STREQI(classname, "worldspawn"))
{
// We only care about the first spawn group's worldspawn because the rest might use prefabs compiled outside of mapping API.
g_mappingApi.apiVersionLoaded = true;
Expand Down Expand Up @@ -531,19 +533,14 @@ void KZ::mapapi::OnCreateLoadingSpawnGroupHook(const CUtlVector<const CEntityKey
continue;
}
const char *classname = ekv->GetString("classname");
if (!V_stricmp(classname, "info_target_server_only"))
if (KZ_STREQI(classname, "info_target_server_only"))
{
Mapi_OnInfoTargetSpawn(ekv);
}
}
}
}

void KZ::mapapi::OnRoundPrestart()
{
g_mappingApi.OnRoundPrestart();
}

void KZ::mapapi::OnSpawn(int count, const EntitySpawnInfo_t *info)
{
if (!info || g_mappingApi.fatalFailure)
Expand Down Expand Up @@ -575,7 +572,7 @@ void KZ::mapapi::OnSpawn(int count, const EntitySpawnInfo_t *info)
continue;
}
const char *classname = info[i].m_pEntity->GetClassname();
if (V_stricmp(classname, "trigger_multiple") == 0)
if (KZ_STREQI(classname, "trigger_multiple"))
{
Mapi_OnTriggerMultipleSpawn(&info[i]);
}
Expand All @@ -592,7 +589,7 @@ void KZ::mapapi::OnSpawn(int count, const EntitySpawnInfo_t *info)
continue;
}
const char *classname = info[i].m_pEntity->GetClassname();
if (V_stricmp(classname, "info_teleport_destination") == 0)
if (KZ_STREQI(classname, "info_teleport_destination"))
{
Mapi_OnInfoTeleportDestinationSpawn(&info[i]);
}
Expand All @@ -605,6 +602,12 @@ void KZ::mapapi::OnSpawn(int count, const EntitySpawnInfo_t *info)
}
}

void KZ::mapapi::OnRoundPreStart()
{
g_mappingApi.triggers.RemoveAll();
g_mappingApi.roundIsStarting = true;
}

void KZ::mapapi::OnRoundStart()
{
g_mappingApi.roundIsStarting = false;
Expand Down Expand Up @@ -748,3 +751,14 @@ bool MappingInterface::IsTriggerATimerZone(CBaseTrigger *trigger)
}
return KZ::mapapi::IsTimerTrigger(kzTrigger->type);
}

bool MappingInterface::GetJumpstatArea(Vector &pos, QAngle &angles)
{
if (g_mappingApi.hasJumpstatArea)
{
pos = g_mappingApi.jumpstatAreaPos;
angles = g_mappingApi.jumpstatAreaAngles;
}

return g_mappingApi.hasJumpstatArea;
}
6 changes: 4 additions & 2 deletions src/kz/mappingapi/kz_mappingapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#define KZ_MAPPING_INTERFACE "KZMappingInterface"

#define KZ_NO_MAPAPI_VERSION -1
#define KZ_NO_MAPAPI_VERSION 0
#define KZ_NO_MAPAPI_COURSE_DESCRIPTOR "Default"
#define KZ_NO_MAPAPI_COURSE_NAME "Main"

Expand Down Expand Up @@ -50,6 +50,7 @@ struct KzMapModifier
bool disableTeleports;
bool disableJumpstats;
bool enableSlide;
float gravity;
};

// KZTRIGGER_ANTI_BHOP
Expand Down Expand Up @@ -131,8 +132,8 @@ namespace KZ::mapapi
// These namespace'd functions are called when relevant game events happen, and are somewhat in order.
void Init();
void OnCreateLoadingSpawnGroupHook(const CUtlVector<const CEntityKeyValues *> *pKeyValues);
void OnRoundPrestart();
void OnSpawn(int count, const EntitySpawnInfo_t *info);
void OnRoundPreStart();
void OnRoundStart();

void CheckEndTimerTrigger(CBaseTrigger *trigger);
Expand Down Expand Up @@ -160,6 +161,7 @@ class MappingInterface
{
public:
virtual bool IsTriggerATimerZone(CBaseTrigger *trigger);
bool GetJumpstatArea(Vector &pos, QAngle &angles);
};

extern MappingInterface *g_pMappingApi;
21 changes: 21 additions & 0 deletions src/kz/misc/kz_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ static_function SCMD_CALLBACK(Command_KzRestart)
return MRES_SUPERCEDE;
}

static_function SCMD_CALLBACK(Command_KzLj)
{
KZPlayer *player = g_pKZPlayerManager->ToPlayer(controller);

Vector destPos;
QAngle destAngles;
if (g_pMappingApi->GetJumpstatArea(destPos, destAngles))
{
player->Teleport(&destPos, &destAngles, &vec3_origin);
}
else
{
player->languageService->PrintChat(true, false, "No Jumpstat Area Found", args->ArgS());
}

return MRES_SUPERCEDE;
}

static_function SCMD_CALLBACK(Command_KzHideWeapon)
{
KZPlayer *player = g_pKZPlayerManager->ToPlayer(controller);
Expand Down Expand Up @@ -314,6 +332,9 @@ void KZ::misc::RegisterCommands()
scmd::RegisterCmd("kz_hide", Command_KzHide);
scmd::RegisterCmd("kz_restart", Command_KzRestart);
scmd::RegisterCmd("kz_r", Command_KzRestart);
scmd::RegisterCmd("kz_lj", Command_KzLj);
scmd::RegisterCmd("kz_ljarea", Command_KzLj);
scmd::RegisterCmd("kz_jsarea", Command_KzLj);
scmd::RegisterCmd("kz_end", Command_KzEnd);
scmd::RegisterCmd("kz_hideweapon", Command_KzHideWeapon);
scmd::RegisterCmd("kz_pc", Command_KzPlayerCheck);
Expand Down
8 changes: 6 additions & 2 deletions src/kz/trigger/callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ void KZTriggerService::OnMappingApiTriggerStartTouchPost(TriggerTouchTracker tra
}
break;

case KZTRIGGER_ANTI_BHOP:
case KZTRIGGER_TELEPORT:
case KZTRIGGER_MULTI_BHOP:
case KZTRIGGER_SINGLE_BHOP:
Expand All @@ -166,6 +165,12 @@ void KZTriggerService::OnMappingApiTriggerTouchPost(TriggerTouchTracker tracker)
bool shouldRecheckTriggers = false;
switch (tracker.kzTrigger->type)
{
case KZTRIGGER_MODIFIER:
{
this->TouchModifierTrigger(tracker);
}
break;

case KZTRIGGER_ANTI_BHOP:
{
this->TouchAntibhopTrigger(tracker);
Expand Down Expand Up @@ -219,7 +224,6 @@ void KZTriggerService::OnMappingApiTriggerEndTouchPost(TriggerTouchTracker track
break;

case KZTRIGGER_TELEPORT:
case KZTRIGGER_ANTI_BHOP:
case KZTRIGGER_MULTI_BHOP:
case KZTRIGGER_SINGLE_BHOP:
case KZTRIGGER_SEQUENTIAL_BHOP:
Expand Down
18 changes: 4 additions & 14 deletions src/kz/trigger/kz_trigger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ void KZTriggerService::TouchTriggersAlongPath(const Vector &start, const Vector

void KZTriggerService::UpdateTriggerTouchList()
{
// reset gravity before all the Touch() calls
this->player->GetPlayerPawn()->m_flGravityScale(1);

if (!this->player->IsAlive() || this->player->GetCollisionGroup() != KZ_COLLISION_GROUP_STANDARD)
{
this->EndTouchAll();
Expand Down Expand Up @@ -293,20 +296,7 @@ bool KZTriggerService::IsManagedByTriggerService(CBaseEntity *toucher, CBaseEnti

bool KZTriggerService::HighFrequencyTouchAllowed(TriggerTouchTracker tracker)
{
if (!tracker.kzTrigger)
{
return false;
}
switch (tracker.kzTrigger->type)
{
case KZTRIGGER_ANTI_BHOP:
case KZTRIGGER_TELEPORT:
case KZTRIGGER_MULTI_BHOP:
case KZTRIGGER_SINGLE_BHOP:
case KZTRIGGER_SEQUENTIAL_BHOP:
return true;
}
return false;
return tracker.kzTrigger;
}

KZTriggerService::TriggerTouchTracker *KZTriggerService::GetTriggerTracker(CBaseTrigger *trigger)
Expand Down
Loading

0 comments on commit efba178

Please sign in to comment.