@@ -16,20 +16,190 @@ Created By:
1616#include " version.h"
1717#include " game.h"
1818
19+ #include " util.h"
1920#include " main.h"
2021
22+ // pointer to start of item list
23+ static gitem_t * s_itemlist = nullptr ;
24+
25+ // ignore G_INFO_VALUEFORKEY if inside GAME_CLIENT_USERINFO_CHANGED
26+ static bool s_in_client_userinfo_changed = false ;
27+
28+
29+ // find entity by userinfo pointer
30+ gentity_t * FindEntityByUserinfo (const char * userinfo) {
31+ int i = 0 ;
32+ gentity_t * ent = nullptr ;
33+ while (i < g_numgents) {
34+ ent = ENT_FROM_NUM (i);
35+ i++;
36+
37+ if (!ent->client || !ent->inuse )
38+ continue ;
39+ // gi.Info_ValueForKey(ent->client->pers.userinfo, "fov", val, sizeof(val));
40+ if (ent->client ->pers .userinfo == userinfo)
41+ return ent;
42+ }
43+
44+ return nullptr ;
45+ }
46+
47+
48+ // find item in s_itemlist by classname
49+ gitem_t * FindItemByClassname (const char * classname) {
50+ if (!s_itemlist)
51+ return nullptr ;
52+
53+ // skip the first blank entry
54+ gitem_t * item = s_itemlist + 1 ;
55+ while (item->classname ) {
56+ if (!strcmp (item->classname , classname))
57+ return item;
58+ item++;
59+ }
60+
61+ return nullptr ;
62+ }
63+
2164
2265intptr_t GAME_vmMain (intptr_t cmd, intptr_t * args) {
66+ // this is called to give the mod level-placed entity info at the start of the map
67+ // unfortunately, stripper may modify weapons if it gets loaded AFTER rocketmod. no real way to fix this
68+ // also, don't do this if rocketmod_enabled is 0
69+ if (cmd == GAME_SPAWN_ENTITIES && QMM_GETINTCVAR (PLID , " rocketmod_enabled" )) {
70+ // change spawn objects:
71+ // weapon_* -> weapon_rocketlauncher
72+ // ammo_* -> ammo_rockets
73+
74+ // if entstring is null or empty, cancel
75+ const char * entstring = (const char *)(args[1 ]);
76+ if (!entstring || !*entstring)
77+ QMM_RET_IGNORED (0 );
78+
79+ std::vector<std::string> tokenlist = tokenlist_from_entstring (entstring);
80+
81+ // the next token is the entity's classname
82+ bool is_classname = false ;
83+ int weapons = 0 ;
84+ int ammo = 0 ;
85+
86+ for (auto & token : tokenlist) {
87+ // if this is the value string for a "classname" key, check it
88+ if (is_classname) {
89+ is_classname = false ;
90+
91+ // if its a weapon entity, make it a rocket launcher
92+ if (token.substr (0 , 7 ) == " weapon_" ) {
93+ token = " weapon_rocketlauncher" ;
94+ weapons++;
95+ }
96+ // if its an ammo entity, make it a rocket ammo pack
97+ else if (token.substr (0 , 5 ) == " ammo_" ) {
98+ token = " ammo_rockets" ;
99+ ammo++;
100+ }
101+ }
102+ // if this token is "classname", then the next token is the actual class name
103+ else if (token == " classname" ) {
104+ is_classname = true ;
105+ }
106+ }
107+
108+ entstring = entstring_from_tokenlist (tokenlist);
109+ args[1 ] = (intptr_t )entstring;
110+ }
111+
112+ // we use G_INFO_VALUEFORKEY with a key of "fov" to determine if a user has respawned. but that is also checked
113+ // inside ClientUserinfoChanged. so we set a flag to ignore it in GAME_CLIENT_USERINFO_CHANGED and clear in _Post
114+ if (cmd == GAME_CLIENT_USERINFO_CHANGED ) {
115+ s_in_client_userinfo_changed = true ;
116+ }
117+
23118 QMM_RET_IGNORED (0 );
24119}
25120
26121
27122intptr_t GAME_syscall (intptr_t cmd, intptr_t * args) {
123+ // this is called with "fov" as key in PutClientInServer after initialization and before ChangeWeapon.
124+ // this is also called in ClientUserinfoChanged but we ignore that one
125+ if (cmd == G_INFO_VALUEFORKEY && !s_in_client_userinfo_changed && QMM_GETINTCVAR (PLID , " rocketmod_enabled" )) {
126+ // pointer to rocket launcher item
127+ static gitem_t * item_rocketlauncher = nullptr ;
128+
129+ // gi.Info_ValueForKey(ent->client->pers.userinfo, "fov", val, sizeof(val));
130+ const char * userinfo = (const char *)args[0 ];
131+ const char * key = (const char *)args[1 ];
132+
133+ // skip if key is not "fov"
134+ if (strcmp (key, " fov" ) != 0 )
135+ QMM_RET_IGNORED (0 );
136+
137+ // we need to check each entity to see which has this userinfo string
138+ gentity_t * ent = FindEntityByUserinfo (userinfo);
139+
140+ // make sure ent is not null and it is in use
141+ if (!ent || !ent->inuse || !ent->client )
142+ QMM_RET_IGNORED (0 );
143+
144+ // make sure ent is a player
145+ if (strcmp (ent->classname , " player" ) != 0 )
146+ QMM_RET_IGNORED (0 );
147+
148+ // most everything after this references the client persistant data, so get a shortcut
149+ client_persistant_t * pers = &ent->client ->pers ;
150+
151+ // make sure client is not a spectator
152+ if (pers->spectator )
153+ QMM_RET_IGNORED (0 );
154+
155+ // find the rocketlauncher item based on first spawned player's blaster:
156+ // the item list starts and ends with empty gitem_t objects, so grab a valid pointer to a blaster,
157+ // and scan back to the beginning null entry. then use FindItemByClassname to find items in the list
158+ if (!item_rocketlauncher) {
159+ gitem_t * item_weapon = pers->weapon ;
160+ if (item_weapon) {
161+ gitem_t * item = item_weapon;
162+ // go back to the beginning of the item list
163+ while (item->classname )
164+ item--;
165+ // save for lookups
166+ s_itemlist = item;
167+ // look up item
168+ item_rocketlauncher = FindItemByClassname (" weapon_rocketlauncher" );
169+ }
170+ }
171+
172+ // still couldn't find rocketlauncher item, cancel
173+ if (!item_rocketlauncher) {
174+ QMM_RET_IGNORED (0 );
175+ }
176+
177+ // remove everything from inventory
178+ pers->inventory .fill (0 );
179+ // set inventory count for rocket launcher to 1
180+ pers->inventory [IT_WEAPON_RLAUNCHER ] = 1 ;
181+ // set weapon to rocket launcher gitem_t
182+ pers->weapon = item_rocketlauncher;
183+ // set ammo to cvar (cap at max)
184+ int start_ammo = (int )QMM_GETINTCVAR (PLID , " rocketmod_ammo" );
185+ if (start_ammo > pers->max_ammo [AMMO_ROCKETS ])
186+ start_ammo = pers->max_ammo [AMMO_ROCKETS ];
187+ pers->inventory [IT_AMMO_ROCKETS ] = start_ammo;
188+ // give chainfist if gauntlet cvar is enabled
189+ if (QMM_GETINTCVAR (PLID , " rocketmod_gauntlet" ))
190+ pers->inventory [IT_WEAPON_CHAINFIST ] = 1 ;
191+ }
28192 QMM_RET_IGNORED (0 );
29193}
30194
31195
32196intptr_t GAME_vmMain_Post (intptr_t cmd, intptr_t * args) {
197+ // we use G_INFO_VALUEFORKEY with a key of "fov" to determine if a user has respawned. but that is also checked
198+ // inside ClientUserinfoChanged. so we set a flag to ignore it in GAME_CLIENT_USERINFO_CHANGED and clear in _Post
199+ if (cmd == GAME_CLIENT_USERINFO_CHANGED ) {
200+ s_in_client_userinfo_changed = false ;
201+ }
202+
33203 QMM_RET_IGNORED (0 );
34204}
35205
0 commit comments