PDA

View Full Version : [TFC] Teleporter arrows


pizzahut
03-16-2015, 20:51
One problem with TFC's teleporters is that you don't know where they lead to. There's a Metamod plugin running on Drippy's 2fort which works great, however it's private AFAIK. I decided to write my own. It sorta works, however it has some flaws.

To get hold of the necessary info, I'm tracking players as they teleport, and make a correlation between teleporters this way. Disadvantage with this method is that when the teleporters are originally built, there is no arrow yet as no one has used the teleporter yet.

I made two versions. The first one draws arrows from the entrance all the way to the exit, across the whole map if necessary. These teleporter routes are only visible by the corresponding team and by spectators.

The 2nd version is the standard approach, that is drawing a single arrow above the tele entrance.

Difficulty with AMXX is that it doesn't give access to certain private entity data of buildings, such as owner of a teleporter and whether it's entrance or exit.

EDIT: What I meant was that reading this thread (https://forums.alliedmods.net/showthread.php?t=46224) I was under the impression that dealing with entity pointers inside the private entity data (e.g. owner of a building) wasn't possible without using a modified TFCX module which is available in the linked thread, but probably outdated. Perhaps this was actually the case at the time of writing (2007). However with the info from Arkshine I now managed to access such data, but haven't integrated this into the plugin yet.

v1

http://images.akamai.steamusercontent.com/ugc/507028274403644263/0FBF9A2F3D57CF925D58579B31665A1FC584A0E2/

v2

http://images.akamai.steamusercontent.com/ugc/507028274403646209/AFC009EC6DC82A21AB1F7A1BC4848F3A5DEF41AC/

There was a bug in the original release, the entity numbers can on some maps be higher than the entity limit (512), e.g. coolfort2. I ran an entity test plugin and it said the maximum allowed entities were 1565. I raised the maximum number to 2048, hopefully this will do!

Arkshine
03-17-2015, 05:42
Difficulty with AMXX is that it doesn't give access to certain private entity data of buildings, such as owner of a teleporter and whether it's entrance or exit.

You can access player's private data with fakemeta module and get/set_pdata_*. Just a matter to find the right offset you need.

Well I can help you a bit.

Looking at IDA, when you create a teleporter, it uses real_owner from CBaseEntity to store the player who is building the teleporter, and m_iType from CTFTeleporter to specify entry/exit, and it seems to be 4 for entry and 5 for exit.

Here a dump from linux binary:


CBasePlayer* m_pPlayer; /* 1720 4 */
float m_flInitialUseDelay; /* 1724 4 */
int m_iType; /* 1728 4 */
int m_iState; /* 1732 4 */
int m_iDestroyed; /* 1736 4 */
int m_iShardIndex; /* 1740 4 */
float m_flMyNextThink; /* 1744 4 */
float m_flDamageDelay; /* 1748 4 */
struct CBaseEntity {
int ()(void) * * _vptr.CBaseEntity; /* 0 4 */
entvars_t * pev; /* 4 4 */
class CBaseEntity * m_pGoalEnt; /* 8 4 */
class CBaseEntity * m_pLink; /* 12 4 */

/* Bitfield combined with previous fields */

TYPEDESCRIPTION m_SaveData[0]; /* 0 0 */

/* XXX 16 bytes hole, try to pack */

struct {
void (*__pfn)(class CBaseEntity *); /* 16 4 */
int __delta; /* 20 4 */
} m_pfnThink; /* 16 8 */
struct {
void (*__pfn)(class CBaseEntity *, class CBaseEntity *); /* 24 4 */
int __delta; /* 28 4 */
} m_pfnTouch; /* 24 8 */
struct {
void (*__pfn)(class CBaseEntity *, class CBaseEntity *, class CBaseEntity *, USE_TYPE, float); /* 32 4 */
int __delta; /* 36 4 */
} m_pfnUse; /* 32 8 */
struct {
void (*__pfn)(class CBaseEntity *, class CBaseEntity *); /* 40 4 */
int __delta; /* 44 4 */
} m_pfnBlocked; /* 40 8 */
/* --- cacheline 1 boundary (64 bytes) --- */
int nextpc; /* 48 4 */
int lastpc; /* 52 4 */
int last_impulse; /* 56 4 */
int armorclass; /* 60 4 */
int tf_items; /* 64 4 */
int tf_items_flags; /* 68 4 */
int no_grenades_1; /* 72 4 */
int no_grenades_2; /* 76 4 */
int tp_grenades_1; /* 80 4 */
int tp_grenades_2; /* 84 4 */
string_t m_iszGrenadeName; /* 88 4 */
BOOL got_aliases; /* 92 4 */
BOOL cheat_check; /* 96 4 */
BOOL is_removed; /* 100 4 */
BOOL is_undercover; /* 104 4 */
BOOL is_building; /* 108 4 */
/* --- cacheline 2 boundary (128 bytes) --- */
BOOL is_detpacking; /* 112 4 */
BOOL is_feigning; /* 116 4 */
BOOL is_unableto_spy_or_teleport; /* 120 4 */
BOOL has_disconnected; /* 124 4 */
BOOL fClientGrenadePrimed; /* 128 4 */
BOOL bRemoveGrenade; /* 132 4 */
short unsigned int m_usBuildingEvent; /* 136 2 */

/* XXX 2 bytes hole, try to pack */

int m_iBuildingEventState; /* 140 4 */
class Vector m_vOldOrigin; /* 144 12 */
int tfstate; /* 156 4 */
int items; /* 160 4 */
int weapon; /* 164 4 */
int m_iPrimedGrenType; /* 168 4 */
class CBaseEntity * observer_list; /* 172 4 */
/* --- cacheline 3 boundary (192 bytes) --- */
class EHANDLE m_pOtherSection; /* 176 8 */
int m_iRightBound; /* 184 4 */
int m_iLeftBound; /* 188 4 */
float cl_no_grenades_1; /* 192 4 */
float cl_no_grenades_2; /* 196 4 */
int * current_ammo; /* 200 4 */
float currentammo; /* 204 4 */
int maxammo_medikit; /* 208 4 */
int ammo_medikit; /* 212 4 */
int maxammo_shells; /* 216 4 */
int ammo_detpack; /* 220 4 */
int maxammo_detpack; /* 224 4 */
int ammo_shells; /* 228 4 */
int maxammo_nails; /* 232 4 */
int ammo_nails; /* 236 4 */
/* --- cacheline 4 boundary (256 bytes) --- */
int maxammo_cells; /* 240 4 */
int ammo_cells; /* 244 4 */
int maxammo_rockets; /* 248 4 */
int ammo_rockets; /* 252 4 */
int items_allowed; /* 256 4 */
float armor_allowed; /* 260 4 */
float maxarmor; /* 264 4 */
int exec_scripts; /* 268 4 */
int exec_map_scripts; /* 272 4 */
int display_class_briefing; /* 276 4 */
int take_screenshots; /* 280 4 */
int local_blood; /* 284 4 */
float weaponmode; /* 288 4 */
float motd; /* 292 4 */
float current_menu; /* 296 4 */
float menu_displaytime; /* 300 4 */
/* --- cacheline 5 boundary (320 bytes) --- */
float menu_refreshtime; /* 304 4 */
float m_flSafetyCheckTime; /* 308 4 */
int team_no; /* 312 4 */
int lives; /* 316 4 */
int infection_team_no; /* 320 4 */
int real_frags; /* 324 4 */
float respawn_time; /* 328 4 */
float suicide_time; /* 332 4 */
class EHANDLE building; /* 336 8 */
float building_wait; /* 344 4 */
class EHANDLE real_owner; /* 348 8 */
float has_dispenser; /* 356 4 */
float has_sentry; /* 360 4 */
float has_entry_teleporter; /* 364 4 */
/* --- cacheline 6 boundary (384 bytes) --- */
float has_exit_teleporter; /* 368 4 */
int weapons_carried; /* 372 4 */
float current_weapon; /* 376 4 */
int last_weapon; /* 380 4 */
float last_weaponmode; /* 384 4 */
float reload_shotgun; /* 388 4 */
float reload_super_shotgun; /* 392 4 */
float reload_nailgun; /* 396 4 */
float reload_super_nailgun; /* 400 4 */
float reload_grenade_launcher; /* 404 4 */
float reload_rocket_launcher; /* 408 4 */
float reload_flamethrower; /* 412 4 */
float reload_incendiary; /* 416 4 */
float heat; /* 420 4 */
float sniper_zoom; /* 424 4 */
float immune_to_check; /* 428 4 */
/* --- cacheline 7 boundary (448 bytes) --- */
float last_saveme_sound; /* 432 4 */
class Vector head_shot_vector; /* 436 12 */
float leg_damage; /* 448 4 */
float cheat_level; /* 452 4 */
float FlashTime; /* 456 4 */
float nailpos; /* 460 4 */
float old_leg_damage; /* 464 4 */
float clientHalluc; /* 468 4 */
float clientTranq; /* 472 4 */
float no_active_nail_grens; /* 476 4 */
float no_active_napalm_grens; /* 480 4 */
float no_active_gas_grens; /* 484 4 */
float numflames; /* 488 4 */
float timecount; /* 492 4 */
/* --- cacheline 8 boundary (512 bytes) --- */
char * flame_id; /* 496 4 */
int undercover_team; /* 500 4 */
int undercover_skin; /* 504 4 */
class EHANDLE undercover_target; /* 508 8 */
float StatusRefreshTime; /* 516 4 */
float StatusBarSize; /* 520 4 */
float StatusBarRes; /* 524 4 */
float ScannerOn; /* 528 4 */
int tf_id; /* 532 4 */
int teamkills; /* 536 4 */
BOOL is_admin; /* 540 4 */
int admin_mode; /* 544 4 */
class EHANDLE admin_use; /* 548 8 */
int ip; /* 556 4 */
/* --- cacheline 9 boundary (576 bytes) --- */
int goal_no; /* 560 4 */
int group_no; /* 564 4 */
int goal_state; /* 568 4 */
int owned_by; /* 572 4 */
string_t teamcheck; /* 576 4 */
string_t owned_by_teamcheck; /* 580 4 */
int goal_activation; /* 584 4 */
int goal_effects; /* 588 4 */
int goal_result; /* 592 4 */
int goal_group; /* 596 4 */
int else_goal; /* 600 4 */
int if_goal_is_active; /* 604 4 */
int if_goal_is_inactive; /* 608 4 */
int if_goal_is_removed; /* 612 4 */
int if_group_is_active; /* 616 4 */
int if_group_is_inactive; /* 620 4 */
/* --- cacheline 10 boundary (640 bytes) --- */
int if_group_is_removed; /* 624 4 */
float search_time; /* 628 4 */
float t_length; /* 632 4 */
int activate_goal_no; /* 636 4 */
int inactivate_goal_no; /* 640 4 */
int remove_goal_no; /* 644 4 */
int restore_goal_no; /* 648 4 */
int activate_group_no; /* 652 4 */
int inactivate_group_no; /* 656 4 */
int remove_group_no; /* 660 4 */
int restore_group_no; /* 664 4 */
BOOL m_bAddBonuses; /* 668 4 */
class Vector goal_min; /* 672 12 */
class Vector goal_max; /* 684 12 */
/* --- cacheline 11 boundary (704 bytes) was 8 bytes ago --- */
string_t replacement_model; /* 696 4 */
int replacement_model_body; /* 700 4 */
int replacement_model_skin; /* 704 4 */
int replacement_model_flags; /* 708 4 */
int has_item_from_group; /* 712 4 */
int hasnt_item_from_group; /* 716 4 */
int remove_item_group; /* 720 4 */
int return_item_no; /* 724 4 */
int if_item_has_moved; /* 728 4 */
int if_item_hasnt_moved; /* 732 4 */
int remove_spawnpoint; /* 736 4 */
int restore_spawnpoint; /* 740 4 */
int remove_spawngroup; /* 744 4 */
int restore_spawngroup; /* 748 4 */
/* --- cacheline 12 boundary (768 bytes) --- */
int display_item_status[4]; /* 752 16 */
string_t team_str_home; /* 768 4 */
string_t team_str_moved; /* 772 4 */
string_t team_str_carried; /* 776 4 */
string_t non_team_str_home; /* 780 4 */
string_t non_team_str_moved; /* 784 4 */
string_t non_team_str_carried; /* 788 4 */
int ex_skill_min; /* 792 4 */
int ex_skill_max; /* 796 4 */
int increase_team[4]; /* 800 16 */
/* --- cacheline 13 boundary (832 bytes) --- */
int increase_team_owned_by; /* 816 4 */
float wait; /* 820 4 */
int count; /* 824 4 */
int axhitme; /* 828 4 */
float drop_time; /* 832 4 */
class Vector redrop_origin; /* 836 12 */
int redrop_count; /* 848 4 */
float attack_finished; /* 852 4 */
float distance; /* 856 4 */
int speed_reduction; /* 860 4 */
float m_flEndRoundTime; /* 864 4 */
string_t m_iszEndRoundMsg_Team1_Win; /* 868 4 */
string_t m_iszEndRoundMsg_Team2_Win; /* 872 4 */
string_t m_iszEndRoundMsg_Team3_Win; /* 876 4 */
/* --- cacheline 14 boundary (896 bytes) --- */
string_t m_iszEndRoundMsg_Team4_Win; /* 880 4 */
string_t m_iszEndRoundMsg_Team1_Lose; /* 884 4 */
string_t m_iszEndRoundMsg_Team2_Lose; /* 888 4 */
string_t m_iszEndRoundMsg_Team3_Lose; /* 892 4 */
string_t m_iszEndRoundMsg_Team4_Lose; /* 896 4 */
string_t m_iszEndRoundMsg_Team1; /* 900 4 */
string_t m_iszEndRoundMsg_Team2; /* 904 4 */
string_t m_iszEndRoundMsg_Team3; /* 908 4 */
string_t m_iszEndRoundMsg_Team4; /* 912 4 */
string_t m_iszEndRoundMsg_OwnedBy; /* 916 4 */
string_t m_iszEndRoundMsg_NonOwnedBy; /* 920 4 */
string_t killtarget; /* 924 4 */
string_t noise4; /* 928 4 */
string_t broadcast; /* 932 4 */
string_t team_broadcast; /* 936 4 */
string_t non_team_broadcast; /* 940 4 */
/* --- cacheline 15 boundary (960 bytes) --- */
string_t owners_team_broadcast; /* 944 4 */
string_t non_owners_team_broadcast; /* 948 4 */
string_t team_drop; /* 952 4 */
string_t non_team_drop; /* 956 4 */
string_t org_broadcast; /* 960 4 */
string_t org_team_broadcast; /* 964 4 */
string_t org_non_team_broadcast; /* 968 4 */
string_t org_owners_team_broadcast; /* 972 4 */
string_t org_non_owners_team_broadcast; /* 976 4 */
string_t org_team_drop; /* 980 4 */
string_t org_non_team_drop; /* 984 4 */
string_t org_message; /* 988 4 */
string_t org_noise3; /* 992 4 */
string_t org_noise4; /* 996 4 */
string_t netname_broadcast; /* 1000 4 */
string_t netname_team_broadcast; /* 1004 4 */
/* --- cacheline 16 boundary (1024 bytes) --- */
string_t netname_non_team_broadcast; /* 1008 4 */
string_t netname_owners_team_broadcast; /* 1012 4 */
string_t netname_team_drop; /* 1016 4 */
string_t netname_non_team_drop; /* 1020 4 */
string_t speak; /* 1024 4 */
string_t AP_speak; /* 1028 4 */
string_t team_speak; /* 1032 4 */
string_t non_team_speak; /* 1036 4 */
string_t owners_team_speak; /* 1040 4 */
string_t non_owners_team_speak; /* 1044 4 */
string_t deathtype; /* 1048 4 */
int all_active; /* 1052 4 */
int item_list; /* 1056 4 */
float delay_time; /* 1060 4 */
BOOL do_triggerwork; /* 1064 4 */
float rv_s_h; /* 1068 4 */
/* --- cacheline 17 boundary (1088 bytes) --- */
float rs_s_h; /* 1072 4 */
float rv_gr; /* 1076 4 */
float rs_gr; /* 1080 4 */
float rv_g; /* 1084 4 */
float rs_g; /* 1088 4 */
float has_abbreviated; /* 1092 4 */
int timer_type; /* 1096 4 */
float invincible_finished; /* 1100 4 */
float invisible_finished; /* 1104 4 */
float super_damage_finished; /* 1108 4 */
float radsuit_finished; /* 1112 4 */
BOOL m_bLostInvincSound; /* 1116 4 */
BOOL m_bLostInvisSound; /* 1120 4 */
BOOL m_bLostSuperSound; /* 1124 4 */
BOOL m_bLostRadSound; /* 1128 4 */
float m_fInvincSound; /* 1132 4 */
/* --- cacheline 18 boundary (1152 bytes) --- */
float m_fSuperSound; /* 1136 4 */

To use these char-based offsets, you need to calculate first the value windows-based.

Generally, for classes derived from CBaseMonster, it's +20 for linux, otherwise +16.

Here CTFTeleporter, should be +20 and +16 for CBaseEntity.

So:

real_owner = 348 - 16 = 332 ; can be used I think with get_pdata_ent (but this native is int-based, so you need to do 332 / 4). In 1.8.3-dev, you can use 332 with get_pdata_ehandle
m_iType = 1728 - 20 = 1708 ; can be used with get_pdata_int (careful too, this is unfortunately int-based too, so you need to do 708 / 4).

Member with type starting with CBase, like m_pPLayer can be retrieved with get_pdata_cbase (hamsandwich module)

Tell if you don't understand something.

pizzahut
03-17-2015, 16:14
Thanks for the info Arkshine. :) I read about offsets in the thread I linked, just wasn't aware that the newer version of AMXX can deal better with this private data.

Due to Valve's mess up with the SteamCMD version of TFC (double rockets, sporadically broken spy disguise, game description not updating, custom sprays not up- or downloading) we're still running a 2012 build of the HLDS and AMXX. Would the SteamCMD compatible version of AMXX work fine with the old HLDS?

version
Protocol version 48
Exe version 1.1.2.2/Stdio (tfc)
Exe build: 11:30:00 Aug 28 2012 (5787)

meta version
Metamod v1.20p36 2013/02/18 (5:13)
by Will Day
http://www.metamod.org/
Patch: Metamod-P (mm-p) v36
by Jussi Kivilinna
http://metamod-p.sourceforge.net/
compiled: Feb 19 2013, 00:06:29 EET (optimized)

amxx version
AMX Mod X 1.8.2-dev (http://www.amxmodx.org)
Authors:
<TAB>David "BAILOPAN" Anderson, Pavol "PM OnoTo" Marko
<TAB>Felix "SniperBeamer" Geyer, Jonny "Got His Gun" Bergstrom
<TAB>Lukasz "SidLuke" Wlasinski, Christian "Basic-Master" Hammacher
<TAB>Borja "faluco" Ferrer, Scott "Damaged Soul" Ehlert
Compiled: Jul 27 2012, 12:58:57
Build ID: 1.8.2-dev 26:81ff4ff3da92
Core mode: JIT+ASM32

Arkshine
03-17-2015, 17:26
AMXX 1.8.2+, likely. The only thing I see is hamdata.ini, you should probably not update this file, values have been changed after the steam update. But it's manually fixable. If you want to use 1.8.2/3 for example, because of some fixes or many missing virtual functions have been added, you can fix file like this: you keep "pev" and "base" and for tfc linux only you add +2 to all values.

By the way, If you want I can see for integrating schnitzelmaker's changes for 1.8.3-dev.

pizzahut
03-18-2015, 16:11
#include <amxmodx>
#include <engine>
#include <fakemeta>

// old tfc_i386.so (2012): 12
// new tfc.so (2013): 16
#define linux_diff 12

public plugin_init()
{
register_plugin("pdata test", "1.4", "pizzahut")
register_clcmd("say test", "say_test")
}

public say_test(id)
{
static tele_id, tele_owner, tele_type
client_print(id, print_chat, "Check console")
tele_id = -1
while (tele_id = find_ent_by_class(tele_id, "building_teleporter"))
{
client_print(id, print_console, "tele id: %d", tele_id)
tele_owner = get_pdata_ent(tele_id, 332, linux_diff)
if (is_valid_ent(tele_owner))
client_print(id, print_console, "owner id: %d", tele_owner)
tele_type = get_pdata_int(tele_id, 427)
switch (tele_type)
{
case 4: client_print(id, print_console, "type: entrance")
case 5: client_print(id, print_console, "type: exit")
}
}
return PLUGIN_HANDLED
}

Arkshine
03-18-2015, 18:56
get_pdata_ent is int-based too. Only _string and the new natives introduced in 1.8.3 are char-based.
And yeah, the linux extra offset you provide in the native, you should / 4 as well (so either 5, which is the default, or 4).

SpannerSpammer
03-20-2015, 23:17
Like Pizzahut, I too have been spoiled by Drippy's teleporter arrows, so I made my own MetaMod based version
a while back. It was a little laggy and the code was not optimized, but it worked. I had never even considered
porting it to AMX because I thought there wasn't any way to manipulate entity edict offsets, I guess I was
wrong. This helped a LOT guys, Thanks. With this I can port my Teleporter Exit Camera function to AMX.


Here's a little something for you Pizzahut, the attached SMA file is some old MM building offset code I just ported.
Note: I don't currently have a Linux box to test it out on so you will need to adjust the linux offset, just let me
know what the value should be. This code works on amx 1.8.2 on Windows, I don't know about later versions.

NOTE: For teleporters use this offset for TYPE

// raw windows offset / 4
#define PD_TELE_STATE 124 / 4

These are the teleporter state values associated with this offet

#define TELE_NOT_PAIRED 0 // Entry or Exit is idle
#define TELE_ENTRY_CHARGE 4 // Entry ONLY (charging)
#define TELE_EXIT_PAIRED 8 // Exit ONLY (paired with Entry)
#define TELE_ENTRY_READY 516 // Entry ONLY (fully charged, ready to use)


I can give you some steps on how to optimize your code if you want.