Skip to content

Commit 3b91e4e

Browse files
committed
completed q2r. removed some debug messages from quake2 and added a comment to G_LINKENTITY section. added cast for a cvar intptr_t->int in q3a code.
1 parent bdb3ea2 commit 3b91e4e

3 files changed

Lines changed: 175 additions & 7 deletions

File tree

src/game_q2r.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

2265
intptr_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

27122
intptr_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

32196
intptr_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

src/game_q3a.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ intptr_t GAME_syscall(intptr_t cmd, intptr_t* args) {
4444
client->ps.stats[STAT_WEAPONS] |= 1 << WP_GAUNTLET;
4545

4646
// give rockets and clear machinegun ammo
47-
client->ps.ammo[WP_ROCKET_LAUNCHER] = QMM_GETINTCVAR(PLID, "rocketmod_ammo");
47+
client->ps.ammo[WP_ROCKET_LAUNCHER] = (int)QMM_GETINTCVAR(PLID, "rocketmod_ammo");
4848
if (client->ps.ammo[WP_ROCKET_LAUNCHER] <= 0)
4949
client->ps.ammo[WP_ROCKET_LAUNCHER] = 10;
5050
client->ps.ammo[WP_MACHINEGUN] = 0;

src/game_quake2.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ intptr_t GAME_vmMain(intptr_t cmd, intptr_t* args) {
5656

5757
std::vector<std::string> tokenlist = tokenlist_from_entstring(entstring);
5858

59-
QMM_WRITEQMMLOG(PLID, QMM_VARARGS(PLID, "%d tokens loaded from engine\n", tokenlist.size()), QMMLOG_DEBUG);
60-
6159
// the next token is the entity's classname
6260
bool is_classname = false;
6361
int weapons = 0;
@@ -85,9 +83,6 @@ intptr_t GAME_vmMain(intptr_t cmd, intptr_t* args) {
8583
}
8684
}
8785

88-
QMM_WRITEQMMLOG(PLID, QMM_VARARGS(PLID, "%d weapons set to rocket launcher\n", weapons), QMMLOG_DEBUG);
89-
QMM_WRITEQMMLOG(PLID, QMM_VARARGS(PLID, "%d ammo set to rockets\n", ammo), QMMLOG_DEBUG);
90-
9186
entstring = entstring_from_tokenlist(tokenlist);
9287
args[1] = (intptr_t)entstring;
9388
}
@@ -107,6 +102,9 @@ intptr_t GAME_vmMain_Post(intptr_t cmd, intptr_t* args) {
107102

108103

109104
intptr_t GAME_syscall_Post(intptr_t cmd, intptr_t* args) {
105+
// this is called near the end of PutClientInServer (respawn) and before ChangeWeapon().
106+
// this is also called in ClientThink regularly, so we check a few things to verify that this is a
107+
// respawn event and not during a regular Think
110108
if (cmd == G_LINKENTITY && QMM_GETINTCVAR(PLID, "rocketmod_enabled")) {
111109
// pointer to rocket launcher item
112110
static gitem_t* item_rocketlauncher = nullptr;
@@ -177,7 +175,7 @@ intptr_t GAME_syscall_Post(intptr_t cmd, intptr_t* args) {
177175
// set weapon to rocket launcher gitem_t
178176
pers->weapon = item_rocketlauncher;
179177
// set ammo to cvar (cap at max)
180-
int start_ammo = QMM_GETINTCVAR(PLID, "rocketmod_ammo");
178+
int start_ammo = (int)QMM_GETINTCVAR(PLID, "rocketmod_ammo");
181179
if (start_ammo > pers->max_rockets)
182180
start_ammo = pers->max_rockets;
183181
pers->inventory[item_index_rockets] = start_ammo;

0 commit comments

Comments
 (0)