--- /dev/null
+ #define NADE_PROJECTILE(i, projectile, trail) do { \
+ this.m_projectile[i] = projectile; \
+ this.m_trail[i] = trail; \
+ } while (0)
+
+ REGISTER_NADE(NORMAL) {
+ this.m_color = '1 1 1';
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE, EFFECT_Null);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_BURN, EFFECT_Null);
++#endif
+ }
+
+ REGISTER_NADE(NAPALM) {
+ this.m_color = '2 0.5 0';
+ this.m_name = _("Napalm grenade");
+ this.m_icon = "nade_napalm";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_NAPALM, EFFECT_TR_ROCKET);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_NAPALM_BURN, EFFECT_SPIDERBOT_ROCKET_TRAIL);
++#endif
+ }
+
+ REGISTER_NADE(ICE) {
+ this.m_color = '0 0.5 2';
+ this.m_name = _("Ice grenade");
+ this.m_icon = "nade_ice";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_ICE, EFFECT_TR_NEXUIZPLASMA);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_ICE_BURN, EFFECT_RACER_ROCKET_TRAIL);
++#endif
+ }
+
+ REGISTER_NADE(TRANSLOCATE) {
+ this.m_color = '1 0 1';
+ this.m_name = _("Translocate grenade");
+ this.m_icon = "nade_translocate";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_TRANSLOCATE, EFFECT_TR_CRYLINKPLASMA);
++#endif
+ }
+
+ REGISTER_NADE(SPAWN) {
+ this.m_color = '1 0.9 0';
+ this.m_name = _("Spawn grenade");
+ this.m_icon = "nade_spawn";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_SPAWN, EFFECT_NADE_TRAIL_YELLOW);
++#endif
+ }
+
+ REGISTER_NADE(HEAL) {
+ this.m_color = '1 0 0';
+ this.m_name = _("Heal grenade");
+ this.m_icon = "nade_heal";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_HEAL, EFFECT_NADE_TRAIL_RED);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_HEAL_BURN, EFFECT_NADE_TRAIL_BURN_RED);
++#endif
+ }
+
+ REGISTER_NADE(MONSTER) {
+ this.m_color = '0.25 0.75 0';
+ this.m_name = _("Monster grenade");
+ this.m_icon = "nade_monster";
++#ifndef MENUQC
+ NADE_PROJECTILE(0, PROJECTILE_NADE_MONSTER, EFFECT_NADE_TRAIL_RED);
+ NADE_PROJECTILE(1, PROJECTILE_NADE_MONSTER_BURN, EFFECT_NADE_TRAIL_BURN_RED);
++#endif
+ }
--- /dev/null
+ #ifndef NADES_ALL_H
+ #define NADES_ALL_H
+
+ #include "../teams.qh"
+
+ .float healer_lifetime;
+ .float healer_radius;
+
+ // use slots 70-100
+ const int PROJECTILE_NADE = 71;
+ const int PROJECTILE_NADE_BURN = 72;
+ const int PROJECTILE_NADE_NAPALM = 73;
+ const int PROJECTILE_NADE_NAPALM_BURN = 74;
+ const int PROJECTILE_NAPALM_FOUNTAIN = 75;
+ const int PROJECTILE_NADE_ICE = 76;
+ const int PROJECTILE_NADE_ICE_BURN = 77;
+ const int PROJECTILE_NADE_TRANSLOCATE = 78;
+ const int PROJECTILE_NADE_SPAWN = 79;
+ const int PROJECTILE_NADE_HEAL = 80;
+ const int PROJECTILE_NADE_HEAL_BURN = 81;
+ const int PROJECTILE_NADE_MONSTER = 82;
+ const int PROJECTILE_NADE_MONSTER_BURN = 83;
+
+ REGISTRY(Nades, BIT(3))
+ REGISTER_REGISTRY(RegisterNades)
+ #define REGISTER_NADE(id) REGISTER(RegisterNades, NADE_TYPE, Nades, id, m_id, NEW(Nade))
+
+ CLASS(Nade, Object)
+ ATTRIB(Nade, m_id, int, 0)
+ ATTRIB(Nade, m_color, vector, '0 0 0')
+ ATTRIB(Nade, m_name, string, _("Grenade"))
+ ATTRIB(Nade, m_icon, string, "nade_normal")
+ ATTRIBARRAY(Nade, m_projectile, int, 2)
+ ATTRIBARRAY(Nade, m_trail, entity, 2)
+ METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) {
+ returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon));
+ }
+ ENDCLASS(Nade)
+
+ REGISTER_NADE(Null);
+
+ #ifdef SVQC
+ bool healer_send(entity this, entity to, int sf);
+ #endif
+
++#ifndef MENUQC
+ entity Nade_FromProjectile(float proj)
+ {
+ FOREACH(Nades, true, LAMBDA(
+ for (int j = 0; j < 2; j++)
+ {
+ if (it.m_projectile[j] == proj) return it;
+ }
+ ));
+ return NADE_TYPE_Null;
+ }
+
+ entity Nade_TrailEffect(int proj, int nade_team)
+ {
+ switch (proj)
+ {
+ case PROJECTILE_NADE: return EFFECT_NADE_TRAIL(nade_team);
+ case PROJECTILE_NADE_BURN: return EFFECT_NADE_TRAIL_BURN(nade_team);
+ }
+
+ FOREACH(Nades, true, LAMBDA(
+ for (int j = 0; j < 2; j++)
+ {
+ if (it.m_projectile[j] == proj)
+ {
+ string trail = it.m_trail[j].eent_eff_name;
+ if (trail) return it.m_trail[j];
+ break;
+ }
+ }
+ ));
+
+ return EFFECT_Null;
+ }
++#endif
+
+ #include "all.inc"
+
+ #endif
/** vehicle hitbox size */
ATTRIB(Vehicle, maxs, vector, '0 0 0')
+ METHOD(Vehicle, display, void(entity this, void(string name, string icon) returns)) {
+ returns(this.vehicle_name, this.m_icon ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon) : string_null);
+ }
++
+ /** (BOTH) setup vehicle data */
+ METHOD(Vehicle, vr_setup, void(Vehicle)) { }
+ /** (SERVER) logic to run every frame */
+ METHOD(Vehicle, vr_think, void(Vehicle)) { }
+ /** (SERVER) called when vehicle dies */
+ METHOD(Vehicle, vr_death, void(Vehicle)) { }
+ /** (BOTH) precaches models/sounds used by this vehicle */
+ METHOD(Vehicle, vr_precache, void(Vehicle)) { }
+ /** (SERVER) called when a player enters this vehicle */
+ METHOD(Vehicle, vr_enter, void(Vehicle)) { }
+ /** (SERVER) called when the vehicle re-spawns */
+ METHOD(Vehicle, vr_spawn, void(Vehicle)) { }
+ /** (SERVER) called when a vehicle hits something */
+ METHOD(Vehicle, vr_impact, void(Vehicle)) { }
+ /** (CLIENT) logic to run every frame */
+ METHOD(Vehicle, vr_hud, void(Vehicle)) { }
ENDCLASS(Vehicle)
+ // vehicle spawn flags (need them here for common registrations)
+ const int VHF_ISVEHICLE = 2; /// Indicates vehicle
+ const int VHF_HASSHIELD = 4; /// Vehicle has shileding
+ const int VHF_SHIELDREGEN = 8; /// Vehicles shield regenerates
+ const int VHF_HEALTHREGEN = 16; /// Vehicles health regenerates
+ const int VHF_ENERGYREGEN = 32; /// Vehicles energy regenerates
+ const int VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage
+ const int VHF_MOVE_GROUND = 128; /// Vehicle moves on gound
+ const int VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound
+ const int VHF_MOVE_FLY = 512; /// Vehicle is airborn
+ const int VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50%
+ const int VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50%
+ const int VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50%
+ const int VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots
+ const int VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle
+
+ // fields:
+ .entity tur_head;
+
#endif
--- /dev/null
- #define REGISTRY_SOURCE(id, arr, count) \
+#ifndef DIALOG_MEDIA_GUIDE_H
+#define DIALOG_MEDIA_GUIDE_H
+#include "../datasource.qc"
+
+#define TOPICS(X) \
+ X(NEW(FreetextSource), _("Guide"), "gametype_tdm") \
+ X(NEW(GametypeSource), _("Gametypes"), "gametype_dm") \
+ X(NEW(WeaponSource), _("Weapons"), "gametype_ka") \
+ X(NEW(ItemSource), _("Items"), "gametype_kh") \
+ X(NEW(BuffSource), _("Buffs"), "gametype_dom") \
+ X(NEW(NadeSource), _("Nades"), "gametype_ft") \
+ X(NEW(MonsterSource), _("Monsters"), "gametype_lms") \
+ X(NEW(VehicleSource), _("Vehicles"), "gametype_rc") \
+ X(NEW(TurretSource), _("Turrets"), "gametype_as") \
+ X(NEW(MutatorSource), _("Mutators"), "gametype_nb") \
+ X(NEW(MapSource), _("Maps"), "gametype_ctf") \
+ if (cvar("developer")) X(NEW(DebugSource), _("Debug"), "gametype_ons") \
+ /**/
+CLASS(TopicSource, DataSource)
+ METHOD(TopicSource, getEntry, entity(TopicSource this, int i, void(string, string) returns)) {
+ int idx = 0;
+ #define TOPIC(src, name, icon) if (idx++ == i) { if (returns) returns(name, icon); return DataSource_true; }
+ TOPICS(TOPIC);
+ #undef TOPIC
+ if (returns) returns("undefined", "undefined");
+ return DataSource_false;
+ }
+ METHOD(TopicSource, reload, int(TopicSource this, string filter)) {
+ int n = 0;
+ #define TOPIC(src, name, icon) n++;
+ TOPICS(TOPIC);
+ #undef TOPIC
+ return n;
+ }
+ENDCLASS(TopicSource)
+
+CLASS(DebugSource, DataSource)
+ .entity nextdebug;
+ entity find_debug() {
+ entity head = NULL, tail = NULL;
+ for (entity it = NULL; (it = nextent(it)); ) {
+ if (!it.instanceOfObject) continue;
+ if (it.instanceOfItem) continue;
+ if (it.instanceOfAnimHost) continue;
+ if (it.instanceOfDataSource) continue;
+ if (it.classname == "Object") continue;
+ if (it.classname == "vtbl") continue;
+ if (!tail) {
+ tail = head = it;
+ } else {
+ tail.nextdebug = it;
+ tail = it;
+ }
+ }
+ return head;
+ }
+ string DebugSource_activeFilter = "";
+ METHOD(DebugSource, getEntry, entity(DebugSource this, int i, void(string, string) returns)) {
+ int idx = 0;
+ entity e;
+ for (e = find_debug(); e; e = e.nextdebug) {
+ if (strstrofs(sprintf("entity %i", e), DebugSource_activeFilter, 0) < 0) continue;
+ if (idx++ == i) break;
+ }
+ if (returns) e.display(e, returns);
+ return e;
+ }
+ METHOD(DebugSource, reload, int(DebugSource this, string filter)) {
+ DebugSource_activeFilter = filter;
+ int idx = 0;
+ entity e;
+ for (e = find_debug(); e; e = e.nextdebug) {
+ if (strstrofs(sprintf("entity %i", e), DebugSource_activeFilter, 0) < 0) continue;
+ idx++;
+ }
+ return idx;
+ }
+ENDCLASS(DebugSource)
+
- METHOD(id, reload, int(id this, string filter)) { return count; } \
++#define REGISTRY_SOURCE(id, arr) \
+CLASS(id, DataSource) \
+ METHOD(id, getEntry, entity(id this, int i, void(string, string) returns)) { \
+ entity e = arr[i]; \
+ if (returns) e.display(e, returns); \
+ return e; \
+ } \
- REGISTRY_SOURCE(FreetextSource, GUIDE_PAGES, GUIDE_PAGES_COUNT)
++ METHOD(id, reload, int(id this, string filter)) { return arr##_COUNT; } \
+ENDCLASS(id)
+
+#include "pages.qh"
- REGISTRY_SOURCE(GametypeSource, MAPINFO_TYPES, MAPINFO_TYPE_COUNT)
++REGISTRY_SOURCE(FreetextSource, GuidePages)
+
+#include "../../../common/mapinfo.qh"
- REGISTRY_SOURCE(ItemSource, ITEMS, ITEM_COUNT)
++REGISTRY_SOURCE(GametypeSource, Gametypes)
+
+#include "../../../common/items/all.qh"
- #include "../../../common/buffs.qh"
- REGISTRY_SOURCE(BuffSource, BUFFS, BUFFS_COUNT)
++REGISTRY_SOURCE(ItemSource, Items)
+
- #include "../../../common/nades.qh"
- REGISTRY_SOURCE(NadeSource, NADES, NADES_COUNT)
++#include "../../../common/buffs/all.qh"
++REGISTRY_SOURCE(BuffSource, Buffs)
+
- REGISTRY_SOURCE(WeaponSource, weapon_info, WEP_COUNT)
++#include "../../../common/nades/all.qh"
++REGISTRY_SOURCE(NadeSource, Nades)
+
+#include "../../../common/weapons/all.qh"
- REGISTRY_SOURCE(MonsterSource, monster_info, MON_COUNT)
++REGISTRY_SOURCE(WeaponSource, Weapons)
+
+#include "../../../common/monsters/all.qh"
- REGISTRY_SOURCE(VehicleSource, vehicle_info, VEH_COUNT)
++REGISTRY_SOURCE(MonsterSource, Monsters)
+
+#include "../../../common/vehicles/all.qh"
- REGISTRY_SOURCE(TurretSource, turret_info, TUR_COUNT)
++REGISTRY_SOURCE(VehicleSource, Vehicles)
+
+#include "../../../common/turrets/all.qh"
- REGISTRY_SOURCE(MutatorSource, MUTATORS, MUTATORS_COUNT)
++REGISTRY_SOURCE(TurretSource, Turrets)
+
+#include "../../../common/mutators/base.qh"
++REGISTRY_SOURCE(MutatorSource, MUTATORS)
+
+CLASS(MapSource, DataSource)
+ METHOD(MapSource, getEntry, entity(MapSource this, int i, void(string, string) returns)) {
+ if (!MapInfo_Get_ByID(i)) return DataSource_false;
+ string path = strcat("/maps/", MapInfo_Map_bspname);
+ string img = draw_PictureSize(path) ? path : "nopreview_map";
+ if (returns) returns(MapInfo_Map_titlestring, img);
+ MapInfo_ClearTemps();
+ return DataSource_true;
+ }
+ METHOD(MapSource, indexOf, int(MapSource this, string s)) {
+ MapInfo_FindName(s);
+ return MapInfo_FindName_firstResult;
+ }
+ METHOD(MapSource, reload, int(MapSource this, string s)) {
+ MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 0);
+ if (s) MapInfo_FilterString(s);
+ return MapInfo_count;
+ }
+ METHOD(MapSource, destroy, void(MapSource this)) { MapInfo_Shutdown(); }
+ENDCLASS(MapSource)
+
+#include "topics.qc"
+#include "entries.qc"
+#include "description.qc"
+#include "../tab.qc"
+CLASS(XonoticGuideTab, XonoticTab)
+ ATTRIB(XonoticGuideTab, rows, float, 21)
+ ATTRIB(XonoticGuideTab, columns, float, 6)
+ ATTRIB(XonoticGuideTab, intendedWidth, float, 1)
+ METHOD(XonoticGuideTab, fill, void(entity));
+ METHOD(XonoticGuideTab, topicChangeNotify, void(entity, entity));
+ METHOD(XonoticGuideTab, entryChangeNotify, void(entity, entity));
+
+ ATTRIB(XonoticGuideTab, topicList, entity, NEW(XonoticTopicList, NEW(TopicSource)))
+ ATTRIB(XonoticGuideTab, entryList, entity, NEW(XonoticEntryList, NULL))
+ ATTRIB(XonoticGuideTab, descriptionPane, entity, NEW(XonoticGuideDescription))
+
+ INIT(XonoticGuideTab) {
+ this.configureDialog(this);
+ }
+ENDCLASS(XonoticGuideTab)
+#endif
+
+#ifdef IMPLEMENTATION
+
+void XonoticGuideTab_fill(entity this)
+{
+ entity topics = this.topicList;
+ topics.onChange = XonoticGuideTab_topicChangeNotify;
+ topics.onChangeEntity = this;
+ entity entries = this.entryList;
+ entries.onChange = XonoticGuideTab_entryChangeNotify;
+ entries.onChangeEntity = this;
+ entity filter = entries.stringFilterBox = makeXonoticInputBox(false, string_null);
+ filter.keyDown = MapList_StringFilterBox_keyDown;
+ filter.onChange = MapList_StringFilterBox_Change;
+ filter.onChangeEntity = entries;
+ entries.controlledTextbox = filter;
+ entity description = this.descriptionPane;
+
+ int
+ col = 0, width = 1.5;
+ this.gotoRC(this, 0, col);
+ this.TD(this, 1, width, makeXonoticHeaderLabel(_("Topic")));
+ this.TR(this);
+ this.TD(this, this.rows - 1, width, topics);
+
+ col += width, width = 2;
+ this.gotoRC(this, 0, col); this.setFirstColumn(this, this.currentColumn);
+ this.TD(this, 1, width, makeXonoticHeaderLabel(_("Entry")));
+ this.TR(this);
+ this.TD(this, this.rows - 1 - 1, width, entries);
+ this.gotoRC(this, this.rows - 1, col);
+ this.TD(this, 1, 0.3, makeXonoticTextLabel(0, _("Filter:")));
+ this.TD(this, 1, width - 0.3, filter);
+
+ col += width, width = 2.5;
+ this.gotoRC(this, 0, col); this.setFirstColumn(this, this.currentColumn);
+ this.TD(this, 1, width, makeXonoticHeaderLabel(_("Description")));
+ this.TR(this);
+ this.TD(this, this.rows - 1, width, description);
+
+ this.topicChangeNotify(topics, this);
+}
+
+void XonoticGuideTab_topicChangeNotify(entity, entity this)
+{
+ entity topics = this.topicList;
+ entity entries = this.entryList;
+ int i = topics.selectedItem;
+ int idx = 0;
+ entity found = NULL;
+ #define TOPIC(src, name, icon) if (idx++ == i) { static entity e; if (!e) e = src; found = e; break; }
+ do { TOPICS(TOPIC); } while (0);
+ #undef TOPIC
+ entries.source = found;
+ entries.refilter(entries);
+ entries.setSelected(entries, 0);
+}
+
+void XonoticGuideTab_entryChangeNotify(entity, entity this)
+{
+ entity desc = this.descriptionPane;
+ entity entries = this.entryList;
+ entity e = entries.source.getEntry(entries.source, entries.selectedItem, func_null);
+ string s = e.describe(e);
+ if (cvar("developer")) { s = sprintf("entity %i\n%s", e, s); }
+ desc.setDescription(desc, s);
+}
+
+#endif