X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fcompat%2Fquake3.qc;h=911ab0f815534dfcebd5e10c35ab7ed658be0d72;hb=ba8ae4694fcaae610fc923f0f13a3b9238a53999;hp=ea951ddceb3e573b9b0137b4f9bc1fb45b8a1435;hpb=f64d037f9d2f38d874422e04d6147c1876a1c829;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index ea951ddce..911ab0f81 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -5,62 +5,102 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include #include #include #include -#include #include -//*********************** -//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons -//*********************** +/*********************** + * QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons + *********************** + + * Map entities NOT handled in this file: + holdable_invulnerability Q3TA buffs/all.inc + holdable_kamikaze Q3TA buffs/all.inc + holdable_teleporter Q3A buffs/all.inc + item_ammoregen Q3TA buffs/all.inc + item_doubler Q3TA buffs/all.inc + item_guard Q3TA buffs/all.inc + item_scout Q3TA buffs/all.inc + item_armor_jacket CPMA quake2.qc + item_flight Q3A buffs/all.inc + item_health Q3A quake.qc + item_health_large Q3A items/spawning.qc + item_health_small Q3A health.qh + item_health_mega Q3A health.qh + item_regen Q3A buffs/all.inc + weapon_machinegun Q3A machinegun.qh + weapon_grenadelauncher Q3A mortar.qh + weapon_rocketlauncher Q3A devastator.qh + * CTF spawnfuncs in sv_ctf.qc + + NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG +*/ + +// SG -> MG || SG +SPAWNFUNC_Q3_COND(weapon_shotgun, ammo_shells, (q3compat & Q3COMPAT_ARENA), WEP_MACHINEGUN, WEP_SHOTGUN) + +// MG -> SG || MG +// Technically we should replace weapon_machinegun with WEP_SHOTGUN if Q3COMPAT_ARENA, but it almost never occurs on Q3 maps +SPAWNFUNC_Q3AMMO_COND(ammo_bullets, (q3compat & Q3COMPAT_ARENA), WEP_SHOTGUN, WEP_MACHINEGUN) -// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG +// GL -> Mortar +SPAWNFUNC_Q3AMMO(ammo_grenades, WEP_MORTAR) -// SG -> SG -SPAWNFUNC_ITEM(ammo_shells, ITEM_Shells) +// Team Arena Proximity Launcher -> Mortar +// It's more accurate to spawn Mine Layer but players prefer Mortar, and weapon_grenadelauncher is usually disabled by "notta" and weapon_prox_launcher placed at the same origin +SPAWNFUNC_Q3(weapon_prox_launcher, ammo_mines, WEP_MORTAR) -// MG -> MG -SPAWNFUNC_ITEM(ammo_bullets, ITEM_Bullets) +// Team Arena Chaingun -> HLAC +SPAWNFUNC_Q3(weapon_chaingun, ammo_belt, WEP_HLAC) -// GL -> Mortar -SPAWNFUNC_ITEM(ammo_grenades, ITEM_Rockets) +// Quake Live Heavy Machine Gun -> HLAC +SPAWNFUNC_Q3(weapon_hmg, ammo_hmg, WEP_HLAC) -// Mines -> Rockets -SPAWNFUNC_WEAPON(weapon_prox_launcher, WEP_MINE_LAYER) -SPAWNFUNC_ITEM(ammo_mines, ITEM_Rockets) +// Team Arena Nailgun -> Crylink || Quake Nailgun -> Electro +SPAWNFUNC_Q3_COND(weapon_nailgun, ammo_nails, cvar("sv_mapformat_is_quake3"), WEP_CRYLINK, WEP_ELECTRO) -// LG -> Lightning -SPAWNFUNC_WEAPON(weapon_lightning, WEP_ELECTRO) -SPAWNFUNC_ITEM(ammo_lightning, ITEM_Cells) +// LG -> Electro +SPAWNFUNC_Q3(weapon_lightning, ammo_lightning, WEP_ELECTRO) // Plasma -> Hagar -SPAWNFUNC_WEAPON(weapon_plasmagun, WEP_HAGAR) -SPAWNFUNC_ITEM(ammo_cells, ITEM_Rockets) +SPAWNFUNC_Q3(weapon_plasmagun, ammo_cells, WEP_HAGAR) // Rail -> Vortex -SPAWNFUNC_WEAPON(weapon_railgun, WEP_VORTEX) -SPAWNFUNC_ITEM(ammo_slugs, ITEM_Cells) +SPAWNFUNC_Q3(weapon_railgun, ammo_slugs, WEP_VORTEX) -// BFG -> Crylink -SPAWNFUNC_WEAPON(weapon_bfg, WEP_CRYLINK) -SPAWNFUNC_ITEM(ammo_bfg, ITEM_Cells) +// BFG -> Crylink || Fireball +SPAWNFUNC_Q3_COND(weapon_bfg, ammo_bfg, cvar_string("g_mod_balance") == "XDF", WEP_CRYLINK, WEP_FIREBALL) + // FIXME: WEP_FIREBALL has no ammo_type field so ammo_bfg is deleted by SPAWNFUNC_BODY // grappling hook -> hook SPAWNFUNC_WEAPON(weapon_grapplinghook, WEP_HOOK) // RL -> RL -SPAWNFUNC_ITEM(ammo_rockets, ITEM_Rockets) +SPAWNFUNC_Q3AMMO(ammo_rockets, WEP_DEVASTATOR) + +// Gauntlet -> Tuba +SPAWNFUNC_ITEM(weapon_gauntlet, WEP_TUBA) // Armor SPAWNFUNC_ITEM(item_armor_body, ITEM_ArmorMega) SPAWNFUNC_ITEM(item_armor_combat, ITEM_ArmorBig) SPAWNFUNC_ITEM(item_armor_shard, ITEM_ArmorSmall) +SPAWNFUNC_ITEM(item_armor_green, ITEM_ArmorMedium) // CCTF + +// Powerups +SPAWNFUNC_ITEM(item_quad, ITEM_Strength) SPAWNFUNC_ITEM(item_enviro, ITEM_Shield) +SPAWNFUNC_ITEM(item_haste, ITEM_Speed) +SPAWNFUNC_ITEM(item_invis, ITEM_Invisibility) // medkit -> armor (we have no holdables) SPAWNFUNC_ITEM(holdable_medkit, ITEM_ArmorBig) @@ -130,17 +170,19 @@ void target_init_use(entity this, entity actor, entity trigger) if (!(this.spawnflags & 8)) { - STAT(STRENGTH_FINISHED, actor) = 0; - STAT(INVINCIBLE_FINISHED, actor) = 0; - if(STAT(BUFFS, actor)) // TODO: make a dropbuffs function to handle this + FOREACH(StatusEffect, it.instanceOfPowerups, + { + it.m_remove(it, actor, STATUSEFFECT_REMOVE_NORMAL); + }); + entity heldbuff = buff_FirstFromFlags(actor); + if(heldbuff) // TODO: make a dropbuffs function to handle this { - int buffid = buff_FirstFromFlags(STAT(BUFFS, actor)).m_id; + int buffid = heldbuff.m_id; Send_Notification(NOTIF_ONE, actor, MSG_MULTI, ITEM_BUFF_DROP, buffid); sound(actor, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); if(!IS_INDEPENDENT_PLAYER(actor)) Send_Notification(NOTIF_ALL_EXCEPT, actor, MSG_INFO, INFO_ITEM_BUFF_LOST, actor.netname, buffid); - STAT(BUFFS, actor) = 0; - STAT(BUFF_TIME, actor) = 0; + buff_RemoveAll(actor, STATUSEFFECT_REMOVE_NORMAL); } } @@ -158,52 +200,35 @@ spawnfunc(target_init) InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET); } -// weapon give ent from defrag +// weapon give ent from Q3 void target_give_init(entity this) { IL_EACH(g_items, it.targetname == this.target, { - if (it.classname == "weapon_devastator") { - SetResourceExplicit(this, RES_ROCKETS, GetResource(this, RES_ROCKETS) + it.count * WEP_CVAR_PRI(devastator, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "devastator"); - } - else if (it.classname == "weapon_vortex") { - SetResourceExplicit(this, RES_CELLS, GetResource(this, RES_CELLS) + it.count * WEP_CVAR_PRI(vortex, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "vortex"); - } - else if (it.classname == "weapon_electro") { - SetResourceExplicit(this, RES_CELLS, GetResource(this, RES_CELLS) + it.count * WEP_CVAR_PRI(electro, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "electro"); - } - else if (it.classname == "weapon_hagar") { - SetResourceExplicit(this, RES_ROCKETS, GetResource(this, RES_ROCKETS) + it.count * WEP_CVAR_PRI(hagar, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "hagar"); - } - else if (it.classname == "weapon_crylink") { - SetResourceExplicit(this, RES_CELLS, GetResource(this, RES_CELLS) + it.count * WEP_CVAR_PRI(crylink, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "crylink"); - } - else if (it.classname == "weapon_mortar") { - SetResourceExplicit(this, RES_ROCKETS, GetResource(this, RES_ROCKETS) + it.count * WEP_CVAR_PRI(mortar, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "mortar"); - } - else if (it.classname == "weapon_shotgun") { - SetResourceExplicit(this, RES_SHELLS, GetResource(this, RES_SHELLS) + it.count * WEP_CVAR_PRI(shotgun, ammo)); // WEAPONTODO - this.netname = cons(this.netname, "shotgun"); + if (it.classname == "item_buff") + { + this.netname = cons(this.netname, it.buffdef.netname); + this.buffs_finished += it.buffs_finished; } - else if (it.classname == "item_armor_mega") - SetResourceExplicit(this, RES_ARMOR, 100); - else if (it.classname == "item_health_mega") - SetResourceExplicit(this, RES_HEALTH, 200); - else if (it.classname == "item_buff") { - entity buff = buff_FirstFromFlags(STAT(BUFFS, it)); - this.netname = cons(this.netname, buff.netname); - STAT(BUFF_TIME, this) = it.count; + else + { + this.ammo_rockets += it.ammo_rockets; + this.ammo_cells += it.ammo_cells; + this.ammo_shells += it.ammo_shells; + this.ammo_nails += it.ammo_nails; + this.invincible_finished += it.invincible_finished; + this.strength_finished += it.strength_finished; + this.speed_finished += it.speed_finished; + this.invisibility_finished += it.invisibility_finished; + this.health += it.health; + this.armorvalue += it.armorvalue; + + this.netname = cons(this.netname, (it.itemdef.m_weapon) ? it.itemdef.m_weapon.netname : it.itemdef.netname); } //remove(it); // removing ents in init functions causes havoc, workaround: - setthink(it, SUB_Remove); - it.nextthink = time; + setthink(it, SUB_Remove); + it.nextthink = time; }); this.spawnflags = 2; this.spawnfunc_checked = true; @@ -247,36 +272,33 @@ spawnfunc(target_fragsFilter) this.use = fragsfilter_use; } -//spawnfunc(item_flight) /* handled by buffs mutator */ -//spawnfunc(item_doubler) /* handled by buffs mutator */ -//spawnfunc(item_haste) /* handled by buffs mutator */ -//spawnfunc(item_health) /* handled in t_quake.qc */ -//spawnfunc(item_health_large) /* handled in items.qc */ -//spawnfunc(item_health_small) /* handled in items.qc */ -//spawnfunc(item_health_mega) /* handled in items.qc */ -//spawnfunc(item_invis) /* handled by buffs mutator */ -//spawnfunc(item_regen) /* handled by buffs mutator */ - -// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now - -.float notteam; -.float notsingle; -.float notfree; -.float notq3a; -.float notta; +.bool notteam; +.bool notsingle; +.bool notfree; +.bool notta; +.bool notvq3; +.bool notcpm; .string gametype; bool DoesQ3ARemoveThisEntity(entity this) { // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY) - if(this.notq3a) - if(!teamplay || g_tdm || g_ctf) + // DeFRaG mappers use "notcpm" or "notvq3" to disable an entity in CPM or VQ3 physics + // Xonotic is usually played with a CPM-based physics so we default to CPM mode + if(cvar_string("g_mod_physics") == "Q3") + { + if(this.notvq3) return true; + } + else if(this.notcpm) + return true; + // Q3 mappers use "notq3a" or "notta" to disable an entity in Q3A or Q3TA + // Xonotic has ~equivalent features to Team Arena if(this.notta) - if (!(!teamplay || g_tdm || g_ctf)) - return true; + return true; + // FIXME: singleplayer does not use maxclients 1 as that would prevent bots if(this.notsingle) if(maxclients == 1) return true; @@ -292,7 +314,7 @@ bool DoesQ3ARemoveThisEntity(entity this) if(this.gametype) { string gametypename; - // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"} + // From ioq3 g_spawn.c: static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester"}; gametypename = "ffa"; if(teamplay) gametypename = "team"; @@ -304,10 +326,26 @@ bool DoesQ3ARemoveThisEntity(entity this) gametypename = "tournament"; if(maxclients == 1) gametypename = "single"; - // we do not have the other types (obelisk, harvester, teamtournament) + // we do not have the other types (obelisk, harvester) if(strstrofs(this.gametype, gametypename, 0) < 0) return true; } return false; } + +int GetAmmoConsumptionQ3(string netname) +// Returns ammo consumed per shot by the primary/default fire mode +// Returns 0 if the netname has no ammo cvar +{ + switch (netname) + { + case "arc": return autocvar_g_balance_arc_beam_ammo; + case "devastator": return autocvar_g_balance_devastator_ammo; + case "machinegun": return autocvar_g_balance_machinegun_sustained_ammo; + case "minelayer": return autocvar_g_balance_minelayer_ammo; + case "seeker": return autocvar_g_balance_seeker_tag_ammo; + default: return cvar(strcat("g_balance_", netname, "_primary_ammo")); + } +} +