]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'bones_was_here/q3compat' into 'master'
authorMario <mario.mario@y7mail.com>
Fri, 10 Sep 2021 15:34:51 +0000 (15:34 +0000)
committerMario <mario.mario@y7mail.com>
Fri, 10 Sep 2021 15:34:51 +0000 (15:34 +0000)
Merge branch bones_was_here/q3compat

Closes #2465

See merge request xonotic/xonotic-data.pk3dir!849

39 files changed:
.gitlab-ci.yml
qcsrc/common/items/item.qh
qcsrc/common/items/item/ammo.qc
qcsrc/common/items/item/health.qh
qcsrc/common/mapobjects/func/button.qc
qcsrc/common/mapobjects/func/door.qc
qcsrc/common/mapobjects/func/plat.qc
qcsrc/common/mapobjects/trigger/hurt.qc
qcsrc/common/mapobjects/trigger/jumppads.qc
qcsrc/common/mapobjects/trigger/multi.qc
qcsrc/common/mutators/mutator/buffs/all.inc
qcsrc/common/mutators/mutator/buffs/buffs.qh
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh
qcsrc/common/mutators/mutator/powerups/powerup/speed.qh
qcsrc/common/physics/player.qc
qcsrc/common/stats.qh
qcsrc/common/weapons/all.qc
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/shockwave.qc
qcsrc/common/weapons/weapon/shotgun.qc
qcsrc/common/weapons/weapon/shotgun.qh
qcsrc/lib/spawnfunc.qh
qcsrc/lib/warpzone/common.qc
qcsrc/server/client.qc
qcsrc/server/compat/quake.qc
qcsrc/server/compat/quake3.qc
qcsrc/server/compat/quake3.qh
qcsrc/server/items/items.qc
qcsrc/server/items/items.qh
qcsrc/server/items/spawning.qc
qcsrc/server/main.qc
qcsrc/server/race.qc
qcsrc/server/teamplay.qc
qcsrc/server/teamplay.qh
qcsrc/server/weapons/spawning.qc
qcsrc/server/world.qc
xonotic-server.cfg

index f5e8ad0d40ad9b8649f735d446a2f8492577b396..3dcd27e44522dcda0eebd6fff804cb140af5159c 100644 (file)
@@ -32,7 +32,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
     - make\r
-    - EXPECT=0052773aed8d3ee62f8a07cd774eec5c\r
+    - EXPECT=d1b8209ef263b8fbb59dbb205049af52\r
     - HASH=$(${ENGINE} -noconfig -nohome +timestamps 1 +exec serverbench.cfg\r
       | tee /dev/stderr\r
       | sed -e 's,^\[[^]]*\] ,,'\r
index 9e322c811d86354b1800e3e9dd79ab975382aa12..350421465459bdd57312511b2cd935c565327a1d 100644 (file)
@@ -69,27 +69,23 @@ const int ITS_GLOW              = BIT(6);
 .float invincible_finished; // ditto
 .float buffs_finished; // ditts
 
-#define spawnfunc_body(item) \
-       if (!Item_IsDefinitionAllowed(item)) \
+#define SPAWNFUNC_BODY(item) \
+       if (item && Item_IsDefinitionAllowed(item)) \
+               StartItem(this, item); \
+       else \
        { \
                startitem_failed = true; \
                delete(this); \
-               return; \
-       } \
-       StartItem(this, item)
+       }
 
 #define SPAWNFUNC_ITEM(name, item) \
        spawnfunc(name) \
        { \
-               spawnfunc_body(item); \
+               SPAWNFUNC_BODY(item) \
        }
 
 #define SPAWNFUNC_ITEM_COND(name, cond, item1, item2) \
-       spawnfunc(name) \
-       { \
-               entity item = (cond) ? item1 : item2; \
-               spawnfunc_body(item); \
-       }
+       SPAWNFUNC_ITEM(name, (cond ? item1 : item2))
 
 #else
 
index 739d94aca1e8b3fb9954ce8a278570f96c6363cf..48ca0ddab6947f2dd320a96d5644983f4a055ee4 100644 (file)
@@ -1,14 +1,2 @@
 #include "ammo.qh"
 
-#ifdef SVQC
-
-METHOD(Shells, m_spawnfunc_hookreplace, GameItem(Shells this, entity e))
-{
-       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
-       {
-               return ITEM_Bullets;
-       }
-       return this;
-}
-
-#endif
index 27f5dafc6e2019e9d8ac13669e323a18451dd11c..0d9992f0ec167575abc3a10f54bc67a29b6f8105 100644 (file)
@@ -27,7 +27,7 @@ void item_healthsmall_init(Pickup this, entity item)
     if(!item.max_health)
         item.max_health = g_pickup_healthsmall_max;
     if(!GetResource(item, RES_HEALTH))
-        SetResourceExplicit(item, RES_HEALTH, g_pickup_healthsmall);
+        SetResourceExplicit(item, RES_HEALTH, q3compat && item.count ? item.count : g_pickup_healthsmall);
 }
 #endif
 
@@ -65,7 +65,7 @@ void item_healthmedium_init(Pickup this, entity item)
     if(!item.max_health)
         item.max_health = g_pickup_healthmedium_max;
     if(!GetResource(item, RES_HEALTH))
-        SetResourceExplicit(item, RES_HEALTH, g_pickup_healthmedium);
+        SetResourceExplicit(item, RES_HEALTH, q3compat && item.count ? item.count : g_pickup_healthmedium);
 }
 #endif
 
@@ -103,7 +103,7 @@ void item_healthbig_init(Pickup this, entity item)
     if(!item.max_health)
         item.max_health = g_pickup_healthbig_max;
     if(!GetResource(item, RES_HEALTH))
-        SetResourceExplicit(item, RES_HEALTH, g_pickup_healthbig);
+        SetResourceExplicit(item, RES_HEALTH, q3compat && item.count ? item.count : g_pickup_healthbig);
 }
 #endif
 
@@ -143,7 +143,7 @@ void item_healthmega_init(Pickup this, entity item)
     if(!item.max_health)
         item.max_health = g_pickup_healthmega_max;
     if(!GetResource(item, RES_HEALTH))
-        SetResourceExplicit(item, RES_HEALTH, g_pickup_healthmega);
+        SetResourceExplicit(item, RES_HEALTH, q3compat && item.count ? item.count : g_pickup_healthmega);
 }
 #endif
 
index 423ac5e7b6d927f9fa2f8cdd42cf9797fe6b2f56..c716b400c86985e1260faa8aeaad2f85ab8bbbb1 100644 (file)
@@ -206,8 +206,8 @@ spawnfunc(func_button)
        if (!this.lip)
                this.lip = 4;
 
-       if(this.wait == -1 && autocvar_sv_q3defragcompat)
-               this.wait = 0.1; // compatibility for q3df: "instant" return
+       if(this.wait < 0 && q3compat)
+               this.wait = 0.1; // compatibility for q3: -1 = return immediately
 
     if(this.noise != "")
         precache_sound(this.noise);
index 05fafa4f026dc0ab881e12a28b6a87ca911dbfed..d3c042ef93dae155463cfff3f6b012803b88f799 100644 (file)
@@ -653,12 +653,25 @@ void door_init_shared(entity this)
        }
 
        // TODO: other soundpacks
-       if (this.sounds > 0)
+       if (this.sounds > 0 || q3compat)
        {
+               // Doors in Q3 always have sounds (they're hard coded in Q3 engine)
                this.noise2 = "plats/medplat1.wav";
                this.noise1 = "plats/medplat2.wav";
        }
 
+       if (q3compat)
+       {
+               // CPMA adds these fields for overriding the engine sounds
+               string s = GetField_fullspawndata(this, "sound_start", true);
+               string e = GetField_fullspawndata(this, "sound_end", true);
+
+               if (s)
+                       this.noise2 = strzone(s);
+               if (e)
+                       this.noise1 = strzone(e);
+       }
+
        // sound when door stops moving
        if(this.noise1 && this.noise1 != "")
        {
@@ -737,7 +750,7 @@ spawnfunc(func_door)
         }
         else if (!this.speed)
         {
-               if (autocvar_sv_q3defragcompat)
+               if (q3compat)
                        this.speed = 400;
                else
                        this.speed = 100;
index 64275a357aaa216e28a34c528da312aaabd441dc..d001fe299dd835821f9815e59aed842b35e551eb 100644 (file)
@@ -76,8 +76,9 @@ spawnfunc(func_plat)
                this.noise1 = "plats/plat2.wav";
        }
 
-       if (this.sounds == 2)
+       if (this.sounds == 2 || q3compat)
        {
+               // Plats in Q3 always have sounds (they're hard coded in Q3 engine)
                this.noise = "plats/medplat1.wav";
                this.noise1 = "plats/medplat2.wav";
        }
@@ -88,6 +89,18 @@ spawnfunc(func_plat)
        if (this.sound2)
                this.noise1 = this.sound2;
 
+       if (q3compat)
+       {
+               // CPMA adds these fields for overriding the engine sounds
+               string s = GetField_fullspawndata(this, "sound_start", true);
+               string e = GetField_fullspawndata(this, "sound_end", true);
+
+               if (s)
+                       this.noise = strzone(s);
+               if (e)
+                       this.noise1 = strzone(e);
+       }
+
        if(this.noise && this.noise != "")
        {
                precache_sound(this.noise);
index 8c21c509c5643419bb937d1d4170a81d711f12df..20c08feb1c052a7737eeedb602c9744c1795ad97 100644 (file)
@@ -11,6 +11,8 @@ void trigger_hurt_use(entity this, entity actor, entity trigger)
 .float triggerhurttime;
 void trigger_hurt_touch(entity this, entity toucher)
 {
+       if (!toucher.takedamage)
+               return;
        if (this.active != ACTIVE_ACTIVE)
                return;
 
@@ -21,11 +23,10 @@ void trigger_hurt_touch(entity this, entity toucher)
        // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
        if (toucher.iscreature)
        {
-               if (toucher.takedamage)
-               if (toucher.triggerhurttime < time)
+               if (time >= toucher.triggerhurttime + ((q3compat && !(this.spawnflags & HURT_SLOW)) ? autocvar_sys_ticrate : 1))
                {
                        EXACTTRIGGER_TOUCH(this, toucher);
-                       toucher.triggerhurttime = time + ((autocvar_sv_q3defragcompat && !(this.spawnflags & HURT_SLOW)) ? 0.1 : 1);
+                       toucher.triggerhurttime = time;
 
                        entity own;
                        own = this.enemy;
@@ -40,14 +41,9 @@ void trigger_hurt_touch(entity this, entity toucher)
        }
        else if(toucher.damagedbytriggers)
        {
-               if(toucher.takedamage)
-               {
-                       EXACTTRIGGER_TOUCH(this, toucher);
-                       Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0');
-               }
+               EXACTTRIGGER_TOUCH(this, toucher);
+               Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0');
        }
-
-       return;
 }
 
 /*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
@@ -66,7 +62,7 @@ spawnfunc(trigger_hurt)
        this.use = trigger_hurt_use;
        this.enemy = world; // I hate you all
        if (!this.dmg)
-               this.dmg = ((autocvar_sv_q3defragcompat) ? 5 : 10000);
+               this.dmg = ((q3compat) ? 5 : 10000);
        if (this.message == "")
                this.message = "was in the wrong place";
        if (this.message2 == "")
index 06842395bdd81f361a90d137bdc4a9e1a2b5e1be..67bb65191cb2b8643189828433dc696f2ceec9d7 100644 (file)
@@ -134,11 +134,8 @@ bool jumppad_push(entity this, entity targ)
                return false;
 
        vector org = targ.origin;
-#ifdef SVQC
-       if(autocvar_sv_q3defragcompat)
-#elif defined(CSQC)
-       if(STAT(Q3DEFRAGCOMPAT))
-#endif
+
+       if(STAT(Q3COMPAT))
        {
                org.z += targ.mins_z;
                org.z += 1; // off by 1!
index 9ce5f52cea877d9a1f556c9fa1ee88b06027fe08..0b0efc21c284d1e82cb6f253ccde21d30c6bdc3e 100644 (file)
@@ -52,6 +52,7 @@ void multi_trigger(entity this)
        {       // we can't just delete(this) here, because this is a touch function
                // called while C code is looping through area links...
                settouch(this, func_null);
+               this.use = func_null;
        }
 }
 
@@ -133,6 +134,7 @@ void multi_reset(entity this)
        setthink(this, func_null);
        this.nextthink = 0;
        this.team = this.team_saved;
+       this.use = multi_use;
 }
 
 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
@@ -167,7 +169,7 @@ spawnfunc(trigger_multiple)
                this.wait = 0;
        this.use = multi_use;
 
-       if(this.wait == -1 && autocvar_sv_q3defragcompat)
+       if(this.wait == -1 && (q3compat & Q3COMPAT_DEFI))
                this.wait = 0.1; // compatibility for q3df: "instant" return
 
        EXACTTRIGGER_INIT;
index 72f54dcd28cb658a9a99c9ad842c5300e16c2218..1572a75e55d9d9dc0599b05946f0c7a3eb84a542 100644 (file)
@@ -2,10 +2,15 @@ string Buff_UndeprecateName(string buffname)
 {
     switch(buffname)
     {
-        case "ammoregen": return "ammo";
-        case "guard": return "resistance";
-        case "revival": case "regen": return "medic";
-        case "jumper": return "jump";
+        case "ammoregen": return "ammo";               // Q3TA ammoregen
+        case "doubler": return "inferno";              // Q3TA doubler
+        case "scout": return "bash";                   // Q3TA scout
+        case "guard": return "resistance";             // Q3TA guard
+        case "revival": case "regen": return "medic";  // WOP revival, Q3A regen
+        case "jumper": return "jump";                  // WOP jumper
+        case "invulnerability": return "vampire";      // Q3TA invulnerability
+        case "kamikaze": return "vengeance";           // Q3TA kamikaze
+        case "teleporter": return "swapper";           // Q3A personal teleporter
         default: return buffname;
     }
 }
@@ -19,7 +24,7 @@ CLASS(AmmoBuff, Buff)
 ENDCLASS(AmmoBuff)
 REGISTER_BUFF(AMMO, NEW(AmmoBuff));
 BUFF_SPAWNFUNCS(ammo, BUFF_AMMO)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_AMMO)
+BUFF_SPAWNFUNC_Q3COMPAT(item_ammoregen, BUFF_AMMO)
 
 CLASS(ResistanceBuff, Buff)
     ATTRIB(ResistanceBuff, m_name, string, _("Resistance"));
@@ -30,7 +35,7 @@ CLASS(ResistanceBuff, Buff)
 ENDCLASS(ResistanceBuff)
 REGISTER_BUFF(RESISTANCE, NEW(ResistanceBuff));
 BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(guard, BUFF_RESISTANCE)
+BUFF_SPAWNFUNC_Q3COMPAT(item_guard, BUFF_RESISTANCE)
 
 CLASS(MedicBuff, Buff)
     ATTRIB(MedicBuff, m_name, string, _("Medic"));
@@ -41,8 +46,8 @@ CLASS(MedicBuff, Buff)
 ENDCLASS(MedicBuff)
 REGISTER_BUFF(MEDIC, NEW(MedicBuff));
 BUFF_SPAWNFUNCS(medic, BUFF_MEDIC)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(regen, BUFF_MEDIC)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(revival, BUFF_MEDIC)
+BUFF_SPAWNFUNC_Q3COMPAT(item_regen, BUFF_MEDIC)
+BUFF_SPAWNFUNC_Q3COMPAT(item_revival, BUFF_MEDIC)
 
 CLASS(BashBuff, Buff)
     ATTRIB(BashBuff, m_name, string, _("Bash"));
@@ -53,7 +58,7 @@ CLASS(BashBuff, Buff)
 ENDCLASS(BashBuff)
 REGISTER_BUFF(BASH, NEW(BashBuff));
 BUFF_SPAWNFUNCS(bash, BUFF_BASH)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(doubler, BUFF_BASH)
+BUFF_SPAWNFUNC_Q3COMPAT(item_scout, BUFF_BASH)
 
 CLASS(VampireBuff, Buff)
     ATTRIB(VampireBuff, m_name, string, _("Vampire"));
@@ -64,6 +69,7 @@ CLASS(VampireBuff, Buff)
 ENDCLASS(VampireBuff)
 REGISTER_BUFF(VAMPIRE, NEW(VampireBuff));
 BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE)
+BUFF_SPAWNFUNC_Q3COMPAT(holdable_invulnerability, BUFF_VAMPIRE)
 
 CLASS(DisabilityBuff, Buff)
     ATTRIB(DisabilityBuff, m_name, string, _("Disability"));
@@ -99,6 +105,7 @@ CLASS(VengeanceBuff, Buff)
 ENDCLASS(VengeanceBuff)
 REGISTER_BUFF(VENGEANCE, NEW(VengeanceBuff));
 BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE)
+BUFF_SPAWNFUNC_Q3COMPAT(holdable_kamikaze, BUFF_VENGEANCE)
 
 CLASS(JumpBuff, Buff)
     ATTRIB(JumpBuff, m_name, string, _("Jump"));
@@ -109,7 +116,7 @@ CLASS(JumpBuff, Buff)
 ENDCLASS(JumpBuff)
 REGISTER_BUFF(JUMP, NEW(JumpBuff));
 BUFF_SPAWNFUNCS(jump, BUFF_JUMP)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(jumper, BUFF_JUMP)
+BUFF_SPAWNFUNC_Q3COMPAT(item_jumper, BUFF_JUMP)
 
 CLASS(InfernoBuff, Buff)
     ATTRIB(InfernoBuff, m_name, string, _("Inferno"));
@@ -120,6 +127,7 @@ CLASS(InfernoBuff, Buff)
 ENDCLASS(InfernoBuff)
 REGISTER_BUFF(INFERNO, NEW(InfernoBuff));
 BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO)
+BUFF_SPAWNFUNC_Q3COMPAT(item_doubler, BUFF_INFERNO)
 
 CLASS(SwapperBuff, Buff)
     ATTRIB(SwapperBuff, m_name, string, _("Swapper"));
@@ -130,6 +138,7 @@ CLASS(SwapperBuff, Buff)
 ENDCLASS(SwapperBuff)
 REGISTER_BUFF(SWAPPER, NEW(SwapperBuff));
 BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER)
+BUFF_SPAWNFUNC_Q3COMPAT(holdable_teleporter, BUFF_SWAPPER)
 
 CLASS(MagnetBuff, Buff)
     ATTRIB(MagnetBuff, m_name, string, _("Magnet"));
@@ -160,4 +169,4 @@ CLASS(FlightBuff, Buff)
 ENDCLASS(FlightBuff)
 REGISTER_BUFF(FLIGHT, NEW(FlightBuff));
 BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(flight, BUFF_FLIGHT)
+BUFF_SPAWNFUNC_Q3COMPAT(item_flight, BUFF_FLIGHT)
index e3e4e5edd3113132574116fca1736efc21e737ef..f4fad84de7fa7f96404a8611cd93c51b67f13a58 100644 (file)
@@ -45,11 +45,13 @@ STATIC_INIT(REGISTER_BUFFS) {
 
 #ifdef SVQC
        .entity buffdef;
+       .int team_forced;
        void buff_Init(entity ent);
        void buff_Init_Compat(entity ent, entity replacement);
        #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \
                this.buffdef = b; \
-               this.team = t; \
+               if(teamplay) \
+                       this.team_forced = t; \
                buff_Init(this); \
        }
        #define BUFF_SPAWNFUNCS(e, b)                       \
@@ -58,11 +60,11 @@ STATIC_INIT(REGISTER_BUFFS) {
                        BUFF_SPAWNFUNC(e##_team2,   b,  NUM_TEAM_2) \
                        BUFF_SPAWNFUNC(e##_team3,   b,  NUM_TEAM_3) \
                        BUFF_SPAWNFUNC(e##_team4,   b,  NUM_TEAM_4)
-       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); }
+       #define BUFF_SPAWNFUNC_Q3COMPAT(o, r) spawnfunc(o) { buff_Init_Compat(this, r); }
 #else
        #define BUFF_SPAWNFUNC(e, b, t)
        #define BUFF_SPAWNFUNCS(e, b)
-       #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r)
+       #define BUFF_SPAWNFUNC_Q3COMPAT(o, r)
 #endif
 
 string Buff_UndeprecateName(string buffname);
index 11176f48f3846271812ef8174358dc7940cbed1c..49f70a8fc9ac2742430112355448b53581ccc0f6 100644 (file)
@@ -129,7 +129,7 @@ void buff_Waypoint_Spawn(entity e)
        if(autocvar_g_buffs_waypoint_distance <= 0) return;
 
        entity buff = e.buffdef;
-       entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff);
+       entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team_forced, e, buff_waypoint, true, RADARICON_Buff);
        wp.wp_extra = buff.m_id;
        WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod);
        e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
@@ -204,7 +204,7 @@ void buff_Touch(entity this, entity toucher)
        if(!IS_PLAYER(toucher))
                return; // incase mutator changed toucher
 
-       if((this.team && DIFF_TEAM(toucher, this))
+       if((this.team_forced && toucher.team != this.team_forced)
        || (STAT(FROZEN, toucher))
        || (toucher.vehicle)
        || (!this.buffdef) // TODO: error out or maybe reset type if this occurs?
@@ -365,7 +365,7 @@ void buff_Think(entity this)
 
        if(this.buff_active)
        {
-               if(this.team && !this.buff_waypoint)
+               if(this.team_forced && !this.buff_waypoint)
                        buff_Waypoint_Spawn(this);
 
                if(this.lifetime && time >= this.lifetime)
@@ -399,7 +399,7 @@ void buff_Reset(entity this)
 bool buff_Customize(entity this, entity client)
 {
        entity player = WaypointSprite_getviewentity(client);
-       if((!this.buff_active || !this.buffdef) || (this.team && DIFF_TEAM(player, this)))
+       if((!this.buff_active || !this.buffdef) || (this.team_forced && player.team != this.team_forced))
        {
                this.alpha = 0.3;
                if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); }
@@ -425,8 +425,6 @@ void buff_Init(entity this)
 {
        if(!cvar("g_buffs")) { delete(this); return; }
 
-       if(!teamplay && this.team) { this.team = 0; }
-
        entity buff = this.buffdef;
 
        if(!buff || !buff_Available(buff))
@@ -475,10 +473,13 @@ void buff_Init(entity this)
 
 void buff_Init_Compat(entity ent, entity replacement)
 {
-       if (ent.spawnflags & 2)
-               ent.team = NUM_TEAM_1;
-       else if (ent.spawnflags & 4)
-               ent.team = NUM_TEAM_2;
+       if (teamplay)
+       {
+               if (ent.spawnflags & 2)
+                       ent.team_forced = NUM_TEAM_1;
+               else if (ent.spawnflags & 4)
+                       ent.team_forced = NUM_TEAM_2;
+       }
 
        ent.buffdef = replacement;
 
index ea2beb584df8f7902ad236d0822029da6e94c24b..7635df6b64eab1510011481e35042e1cbcb8f460 100644 (file)
@@ -66,9 +66,9 @@ float autocvar_g_buffs_luck_damagemultiplier = 3;
 .float buff_effect_delay;
 
 // buff definitions
-.float buff_active;
+.bool buff_active;
 .float buff_activetime;
-.float buff_activetime_updated;
+.bool buff_activetime_updated;
 .entity buff_waypoint;
 .entity oldbuffs; // for updating effects
 .float buff_shield; // delay for players to keep them from spamming buff pickups
index 8120b25457aa643f876583d5b1bc32c5350a463a..d920aab9f22ed3cf8710e67501e19c054a01b5d2 100644 (file)
@@ -52,8 +52,6 @@ REGISTER_ITEM(Invisibility, Powerup) {
 }
 
 SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
-// compat
-SPAWNFUNC_ITEM(item_invis, ITEM_Invisibility)
 
 CLASS(Invisibility, Powerups)
     ATTRIB(Invisibility, netname, string, "invisibility");
index 1f56bd0ad8ea68e2b66c83335208bb6db35ff1f6..5b71df8083bd88e45e698fb5b88d6c526280d984 100644 (file)
@@ -53,9 +53,6 @@ REGISTER_ITEM(Speed, Powerup) {
 }
 
 SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
-// compat
-SPAWNFUNC_ITEM(item_haste, ITEM_Speed)
-SPAWNFUNC_ITEM(item_scout, ITEM_Speed)
 
 CLASS(Speed, Powerups)
     ATTRIB(Speed, netname, string, "speed");
index d562ab64bde15bffc3923e8e385553bd59fbdc64..2b3c51374dfad689a9da0e46c520bb5abe37db34 100644 (file)
@@ -54,13 +54,13 @@ void Physics_UpdateStats(entity this)
             : 0;
           STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod;
         }
-       bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox; // NOTE: these hitboxes are off by 1 due to engine differences
-       STAT(PL_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_mins;
-       STAT(PL_MAX, this) = (q3dfcompat) ? '15 15 36' : autocvar_sv_player_maxs;
-       STAT(PL_VIEW_OFS, this) = (q3dfcompat) ? '0 0 30' : autocvar_sv_player_viewoffset;
-       STAT(PL_CROUCH_MIN, this) = (q3dfcompat) ? '-15 -15 -20' : autocvar_sv_player_crouch_mins;
-       STAT(PL_CROUCH_MAX, this) = (q3dfcompat) ? '15 15 20' : autocvar_sv_player_crouch_maxs;
-       STAT(PL_CROUCH_VIEW_OFS, this) = (q3dfcompat) ? '0 0 16' : autocvar_sv_player_crouch_viewoffset;
+       bool q3hb = q3compat && autocvar_sv_q3compat_changehitbox;
+       STAT(PL_MIN, this) = (q3hb) ? '-15 -15 -20' : autocvar_sv_player_mins;
+       STAT(PL_MAX, this) = (q3hb) ? '15 15 36' : autocvar_sv_player_maxs;
+       STAT(PL_VIEW_OFS, this) = (q3hb) ? '0 0 30' : autocvar_sv_player_viewoffset;
+       STAT(PL_CROUCH_MIN, this) = (q3hb) ? '-15 -15 -20' : autocvar_sv_player_crouch_mins;
+       STAT(PL_CROUCH_MAX, this) = (q3hb) ? '15 15 20' : autocvar_sv_player_crouch_maxs;
+       STAT(PL_CROUCH_VIEW_OFS, this) = (q3hb) ? '0 0 16' : autocvar_sv_player_crouch_viewoffset;
 
        // old stats
        // fix some new settings
index 12879a7531b1acd3f6bff24b18654d54bd1569cb..463d98c850157f9c595dd4e57759ce75fb57c60c 100644 (file)
@@ -4,6 +4,7 @@
 
 #ifdef SVQC
 #include <server/client.qh>
+#include <server/compat/quake3.qh>
 #include <server/main.qh>
 #include <common/gamemodes/sv_rules.qh>
 #include <common/mapobjects/teleporters.qh>
@@ -352,10 +353,7 @@ bool autocvar_sv_slick_applygravity;
 #endif
 REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity)
 
-#ifdef SVQC
-bool autocvar_sv_q3defragcompat;
-#endif
-REGISTER_STAT(Q3DEFRAGCOMPAT, bool, autocvar_sv_q3defragcompat)
+REGISTER_STAT(Q3COMPAT, int, q3compat)
 
 #ifdef SVQC
 #include "physics/movetypes/movetypes.qh"
index e97de364bbab9d96a011e55151de1c59c60eb5dc..7ef3cc1e8d7a5ee18ff37591f899d635d8e0ee6d 100644 (file)
@@ -230,6 +230,23 @@ string GetAmmoName(int ammotype)
        }
 }
 
+entity GetAmmoItem(int ammotype)
+{
+       switch (ammotype)
+       {
+               case RES_SHELLS:  return ITEM_Shells;
+               case RES_BULLETS: return ITEM_Bullets;
+               case RES_ROCKETS: return ITEM_Rockets;
+               case RES_CELLS:   return ITEM_Cells;
+               case RES_PLASMA:  return ITEM_Plasma;
+               case RES_FUEL:    return ITEM_JetpackFuel;
+       }
+       LOG_WARNF("Invalid ammo type %d ", ammotype);
+       return NULL;
+       // WEAPONTODO: use this generic func to reduce duplication ?
+       // GetAmmoPicture  GetAmmoName  notif_arg_item_wepammo  ammo_pickupevalfunc ?
+}
+
 #ifdef CSQC
 int GetAmmoTypeFromNum(int i)
 {
index 67f646cbe5b328dbbb8d7671cbef65db65189fc9..83f3f4a0febd438e9f817f3ba9010bd747f78992 100644 (file)
@@ -136,11 +136,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e);
     spawnfunc(name) { weapon_defaultspawnfunc(this, weapon); }
 
 #define SPAWNFUNC_WEAPON_COND(name, cond, wep1, wep2) \
-    spawnfunc(name) \
-    { \
-        entity wep = (cond) ? wep1 : wep2; \
-        weapon_defaultspawnfunc(this, wep); \
-    }
+       SPAWNFUNC_WEAPON(name, (cond ? wep1 : wep2))
 
 #else
 
@@ -231,6 +227,8 @@ string GetAmmoPicture(int ammotype);
 
 string GetAmmoName(int ammotype);
 
+entity GetAmmoItem(int ammotype);
+
 #ifdef CSQC
 int GetAmmoTypeFromNum(int i);
 int GetAmmoStat(int ammotype);
index 5444980ca89ba2efc129dcfc25141a8503e681aa..6b357968a7c8ac993ac234b82b622a3d55314d39 100644 (file)
@@ -3,18 +3,6 @@
 REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
 
 #ifdef SVQC
-// enable when shockwave replaces shotgun
-#if 0
-METHOD(Shockwave, m_spawnfunc_hookreplace, Weapon(Shockwave this, entity e))
-{
-       //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO
-       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
-       {
-               return WEP_MACHINEGUN;
-       }
-       return this;
-}
-#endif
 
 const float MAX_SHOCKWAVE_HITS = 10;
 //#define DEBUG_SHOCKWAVE
index dcde58a303554a79794e6c16fd48b0bd8295652b..bc1ce89479b4390b67dcf02ed87fd0468a02a3bf 100644 (file)
@@ -5,15 +5,6 @@
 // enable to debug melee range
 //#define SHOTGUN_MELEEDEBUG
 
-METHOD(Shotgun, m_spawnfunc_hookreplace, Weapon(Shotgun this, entity e))
-{
-       if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
-       {
-               return WEP_MACHINEGUN;
-       }
-       return this;
-}
-
 void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary, float ammocount, float damage, float bullets, float spread, float solidpenetration, float force, entity bullet_trail_effect)
 {
        W_DecreaseAmmo(thiswep, actor, ammocount, weaponentity);
index 1c2d280102af276cdbca8e18baab892ca9910f61..d99a371f34cbcd593aabb50cd789d136ab320e2e 100644 (file)
@@ -56,4 +56,3 @@ CLASS(Shotgun, Weapon)
 ENDCLASS(Shotgun)
 REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun));
 
-SPAWNFUNC_WEAPON(weapon_shotgun, WEP_SHOTGUN)
index 5d35089e5ab1daf83c1441691f5a3538d63c4555..e29939909734c14f241d15bb1e449552b492f0c9 100644 (file)
@@ -7,7 +7,8 @@
 noref bool require_spawnfunc_prefix;
 .bool spawnfunc_checked;
 /** Not for production use, provides access to a dump of the entity's fields when it is parsed from map data */
-//noref string __fullspawndata;
+noref string __fullspawndata;
+.string fullspawndata;
 
 // Optional type checking; increases compile time too much to be enabled by default
 #if 0
@@ -179,6 +180,12 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, noise2) \
                FIELD_SCALAR(fld, noise3) \
                FIELD_SCALAR(fld, noise) \
+               FIELD_SCALAR(fld, notcpm) \
+               FIELD_SCALAR(fld, notfree) \
+               FIELD_SCALAR(fld, notsingle) \
+               FIELD_SCALAR(fld, notta) \
+               FIELD_SCALAR(fld, notteam) \
+               FIELD_SCALAR(fld, notvq3) \
                FIELD_SCALAR(fld, phase) \
                FIELD_SCALAR(fld, platmovetype) \
                FIELD_SCALAR(fld, race_place) \
@@ -286,6 +293,11 @@ noref bool __spawnfunc_first;
                this.classname = #id; \
                if (!this.spawnfunc_checked) { \
                        _checkWhitelisted(this, #id); \
+                       if (__fullspawndata) { \
+                               /* not supported in old DP */ \
+                               /* must be read inside the real spawnfunc */ \
+                               this.fullspawndata = __fullspawndata; \
+                       } \
                        this.spawnfunc_checked = true; \
                        if (this) { \
                                /* not worldspawn, delay spawn */ \
index 66658a3901d1dd7837e5883675a328c26e35b927..46710db66c92c041fd447a2ea3e00c2edde83356 100644 (file)
@@ -798,10 +798,13 @@ entity WarpZone_RefSys_SpawnSameRefSys(entity me)
 bool WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher)
 {
        vector emin = toucher.absmin, emax = toucher.absmax;
-       // the engine offsets absolute bounding boxes by a single quake unit
-       // we must undo that here to allow accurate touching
-       emin += '1 1 1';
-       emax -= '1 1 1';
+       if(STAT(Q3COMPAT))
+       {
+               // DP's tracebox enlarges absolute bounding boxes by a single quake unit
+               // we must undo that here to allow accurate touching
+               emin += '1 1 1';
+               emax -= '1 1 1';
+       }
        return !WarpZoneLib_BoxTouchesBrush(emin, emax, this, toucher);
 }
 
index dadc04b5bae7cda9766655e88f721ec846bd684f..804025ace1e5efab8a88d140ff2db5808a72b2eb 100644 (file)
@@ -618,8 +618,7 @@ void PutPlayerInServer(entity this)
        this.respawn_flags = 0;
        this.respawn_time = 0;
        STAT(RESPAWN_TIME, this) = 0;
-       bool q3dfcompat = autocvar_sv_q3defragcompat && autocvar_sv_q3defragcompat_changehitbox;
-       this.scale = ((q3dfcompat) ? 0.9 : autocvar_sv_player_scale);
+       this.scale = ((q3compat && autocvar_sv_q3compat_changehitbox) ? 0.9 : autocvar_sv_player_scale);
        this.fade_time = 0;
        this.pain_finished = 0;
        this.pushltime = 0;
index a49b85f18f60b0150898a491d78ac86a0879fec0..cd5f78b746450e31d2301dd7a584d6395f2d233c 100644 (file)
@@ -4,15 +4,17 @@
 #include <common/weapons/_all.qh>
 #include <common/weapons/_all.qh>
 
-//***********************
-//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
-//***********************
-SPAWNFUNC_WEAPON(weapon_nailgun, WEP_ELECTRO)
+/***********************
+ * QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
+ ***********************
+ weapon_nailgun handled in quake3.qc
+ item_armor1 handled in items/spawning.qc
+*/
+
 SPAWNFUNC_WEAPON(weapon_supernailgun, WEP_HAGAR)
 SPAWNFUNC_WEAPON(weapon_supershotgun, WEP_MACHINEGUN)
 
 SPAWNFUNC_ITEM(item_spikes, ITEM_Bullets)
-//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);}  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
 SPAWNFUNC_ITEM(item_armor2, ITEM_ArmorMega)
 SPAWNFUNC_ITEM(item_armorInv, ITEM_ArmorMega) // TODO: make sure we actually want this
 SPAWNFUNC_ITEM_COND(item_health, (this.spawnflags & 2), ITEM_HealthMega, ITEM_HealthMedium)
index 121df69265166023ff4188cf1cd6810183d184aa..ac2409cc0dc4b0718e5105dd1cacd2593957ad87 100644 (file)
 #include <server/resources.qh>
 #include <server/world.qh>
 
-//***********************
-//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)
@@ -163,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 = it.buffdef;
-                       this.netname = cons(this.netname, buff.netname);
-                       this.buffs_finished = 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;
@@ -252,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 powerups 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 powerups 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;
@@ -297,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";
@@ -309,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"));
+       }
+}
+
index a9b90abadc44b1ed9bf0ca7d8d0b0d27cc9f5888..d2c547b6f6e9d3bacce6388c7f5eed703c642be1 100644 (file)
@@ -1,8 +1,40 @@
 #pragma once
 
-bool autocvar_sv_q3acompat_machineshotgunswap;
-bool autocvar_sv_q3defragcompat_changehitbox = false;
+int q3compat = 0;
+#define Q3COMPAT_ARENA BIT(0)
+#define Q3COMPAT_DEFI BIT(1)
+
+bool autocvar_sv_q3compat_changehitbox;
 
 bool DoesQ3ARemoveThisEntity(entity this);
+int GetAmmoConsumptionQ3(string netname);
 
 .int fragsfilter_cnt;
+
+/* We tell the ammo spawnfunc which weapon will use the ammo so it can
+ * calculate the amount required for the number of shots in the count field,
+ * and so the type can be looked up rather than specified in quake3.qc
+ */
+// Ammo only, unconditional
+#define SPAWNFUNC_Q3AMMO(ammo_classname, xonwep) \
+       spawnfunc(ammo_classname) \
+       { \
+               if(this.count && xonwep.ammo_type) \
+                       SetResource(this, xonwep.ammo_type, this.count * GetAmmoConsumptionQ3(xonwep.netname)); \
+       SPAWNFUNC_BODY(GetAmmoItem(xonwep.ammo_type)) \
+       }
+
+// Ammo only, conditional
+#define SPAWNFUNC_Q3AMMO_COND(ammo_classname, cond, xonwep1, xonwep0) \
+       SPAWNFUNC_Q3AMMO(ammo_classname, (cond ? xonwep1 : xonwep0))
+
+// Weapon & ammo, unconditional
+#define SPAWNFUNC_Q3(weapon_classname, ammo_classname, xonwep) \
+       SPAWNFUNC_WEAPON(weapon_classname, xonwep) \
+       SPAWNFUNC_Q3AMMO(ammo_classname, xonwep)
+
+// Weapon & ammo, conditional
+#define SPAWNFUNC_Q3_COND(weapon_classname, ammo_classname, cond, xonwep1, xonwep0) \
+       SPAWNFUNC_WEAPON_COND(weapon_classname, cond, xonwep1, xonwep0) \
+       SPAWNFUNC_Q3AMMO_COND(ammo_classname, cond, xonwep1, xonwep0)
+
index db9b3fece9609190313bf71a0693cf062ff8212d..932db7a553dd2f663c7a1549d15cfa52f54d160c 100644 (file)
@@ -1057,19 +1057,19 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
                if(autocvar_spawn_debug >= 2)
                {
-            // why not flags & fl_item?
-                   FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
-                LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
-                LOG_TRACE(" vs ", it.netname, vtos(it.origin));
-                error("Mapper sucks.");
-            });
+                       // why not flags & fl_item?
+                       FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
+                               LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
+                               LOG_TRACE(" vs ", it.netname, vtos(it.origin));
+                               error("Mapper sucks.");
+                       });
                        this.is_item = true;
                }
 
                weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid));
 
-               if (   def.instanceOfPowerup
-                       || def.instanceOfWeaponPickup
+               if (        def.instanceOfPowerup
+                       ||  def.instanceOfWeaponPickup
                        || (def.instanceOfHealth && def != ITEM_HealthSmall)
                        || (def.instanceOfArmor && def != ITEM_ArmorSmall)
                        || (itemid & (IT_KEY1 | IT_KEY2))
@@ -1146,18 +1146,21 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
 void StartItem(entity this, GameItem def)
 {
-    def = def.m_spawnfunc_hookreplace(def, this);
-    if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
-    {
-        delete(this);
-        return;
-    }
-    this.classname = def.m_canonical_spawnfunc;
-    _StartItem(
-       this,
-       this.itemdef = def,
-       def.m_respawntime(), // defaultrespawntime
-       def.m_respawntimejitter() // defaultrespawntimejitter
+       def = def.m_spawnfunc_hookreplace(def, this);
+
+       if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
+       {
+               delete(this);
+               return;
+       }
+
+       this.classname = def.m_canonical_spawnfunc;
+
+       _StartItem(
+               this,
+               this.itemdef = def,
+               def.m_respawntime(), // defaultrespawntime
+               def.m_respawntimejitter() // defaultrespawntimejitter
        );
 }
 
index ff2d138437658ed1e941f2c06dc5f0b10eb15a33..7a472aa94cd7f9ccbec9ab96548ca71f508a1227 100644 (file)
@@ -28,6 +28,7 @@ const float ITEM_RESPAWN_TICKS = 10;
 
 .float max_armorvalue;
 .float pickup_anyway;
+.int count;
 
 .float scheduledrespawntime;
 .float respawntime;
index fc8338f38d1f67db11a986ca1aa4571ed31d726c..11bf67f5e8650a16133728aa35aa99ba15580b85 100644 (file)
@@ -132,8 +132,8 @@ void Item_SetExpiring(entity item, bool expiring)
 
 // Compatibility spawn functions
 
-// FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-SPAWNFUNC_ITEM(item_armor1, ITEM_ArmorSmall)
+// in Quake this is green armor, in Xonotic maps it is an armor shard
+SPAWNFUNC_ITEM_COND(item_armor1, cvar("sv_mapformat_is_quake3"), ITEM_ArmorSmall, ITEM_ArmorMedium)
 
 SPAWNFUNC_ITEM(item_armor25, ITEM_ArmorMega)
 
@@ -146,5 +146,3 @@ SPAWNFUNC_ITEM(item_health25, ITEM_HealthMedium)
 SPAWNFUNC_ITEM(item_health_large, ITEM_HealthBig)
 
 SPAWNFUNC_ITEM(item_health100, ITEM_HealthMega)
-
-SPAWNFUNC_ITEM(item_quad, ITEM_Strength)
index 73f473ae979752c412265e06e3bf596daf66dee8..cf91b9de4bafd7d6b250a860732702a82222f409 100644 (file)
@@ -365,7 +365,7 @@ void SV_OnEntityPreSpawnFunction(entity this)
                return;
        }
 
-       if (DoesQ3ARemoveThisEntity(this)) {
+       if (q3compat && DoesQ3ARemoveThisEntity(this)) {
                delete(this);
                return;
        }
@@ -397,6 +397,62 @@ void SV_OnEntityPreSpawnFunction(entity this)
        }
 }
 
+string GetField_fullspawndata(entity e, string f, ...)
+/* Retrieves the value of a map entity field from fullspawndata
+ * This bypasses field value changes made by the engine,
+ * eg string-to-float and escape sequence substitution.
+ *
+ * Avoids the need to declare fields just to read them once :)
+ *
+ * Returns the last instance of the field to match DarkPlaces behaviour.
+ * Path support: converts \ to / and tests the file if a third (bool, true) arg is passed.
+ * Returns string_null if the entity does not have the field, or the file is not in the VFS.
+ *
+ * FIXME: entities with //comments are not supported.
+ */
+{
+       string v = string_null;
+
+       if (!e.fullspawndata)
+       {
+               //LOG_WARNF("^1EDICT %s (classname %s) has no fullspawndata, engine lacks support?", ftos(num_for_edict(e)), e.classname);
+               return v;
+       }
+
+       if (strstrofs(e.fullspawndata, "//", 0) >= 0)
+       {
+               // tokenize and tokenize_console return early if "//" is reached,
+               // which can leave an odd number of tokens and break key:value pairing.
+               LOG_WARNF("^1EDICT %s fullspawndata contains unsupported //comment^7%s", ftos(num_for_edict(e)), e.fullspawndata);
+               return v;
+       }
+
+       //print(sprintf("%s(EDICT %s, FIELD %s)\n", __FUNC__, ftos(num_for_edict(e)), f));
+       //print(strcat("FULLSPAWNDATA:", e.fullspawndata, "\n"));
+
+       // tokenize treats \ as an escape, but tokenize_console returns the required literal
+       for (int t = tokenize_console(e.fullspawndata) - 3; t > 0; t -= 2)
+       {
+               //print(sprintf("\tTOKEN %s:%s\t%s:%s\n", ftos(t), ftos(t + 1), argv(t), argv(t + 1)));
+               if (argv(t) == f)
+               {
+                       v = argv(t + 1);
+                       break;
+               }
+       }
+
+       //print(strcat("RESULT: ", v, "\n\n"));
+
+       if (v && ...(0, bool) == true)
+       {
+               v = strreplace("\\", "/", v);
+               if (whichpack(v) == "")
+                       return string_null;
+       }
+
+       return v;
+}
+
 void WarpZone_PostInitialize_Callback()
 {
        // create waypoint links for warpzones
index c940afb94dc4af026a48549bf321fb9d14c32744..5ccb607e926ce13362fc2d11e4c1e444b7bdeb01 100644 (file)
@@ -839,9 +839,35 @@ bool race_waypointsprite_visible_for_player(entity this, entity player, entity v
                return false;
 }
 
+void defrag_waypointsprites(entity targeted, entity checkpoint)
+{
+       // bones_was_here: spawn a waypoint for every entity with a bmodel
+       // that directly or indirectly targets this checkpoint
+       // (anything a player could touch or shoot to activate this cp)
+
+       entity s = WP_RaceCheckpoint;
+       if (checkpoint.classname == "target_startTimer")
+               s = WP_RaceStart;
+       else if (checkpoint.classname == "target_stopTimer")
+               s = WP_RaceFinish;
+
+       for (entity t = findchain(target, targeted.targetname); t; t = t.chain)
+       {
+               if (t.modelindex)
+               {
+                       WaypointSprite_SpawnFixed(s, (t.absmin + t.absmax) * 0.5, t, sprite, RADARICON_NONE);
+                       t.sprite.realowner = checkpoint;
+                       t.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
+               }
+
+               if (t.targetname)
+                       defrag_waypointsprites(t, checkpoint);
+       }
+}
+
 void trigger_race_checkpoint_verify(entity this)
 {
-    static bool have_verified;
+       static bool have_verified;
        if (have_verified) return;
        have_verified = true;
 
@@ -859,7 +885,7 @@ void trigger_race_checkpoint_verify(entity this)
                        pl_race_place = 0;
                        if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for respawning in race) - bailing out"));
-            }
+                       }
 
                        if (i == 0) {
                                // qualifying only
@@ -867,7 +893,7 @@ void trigger_race_checkpoint_verify(entity this)
                                pl_race_place = race_lowest_place_spawn;
                                if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                        error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
-                }
+                               }
 
                                // race only (initial spawn)
                                g_race_qualifying = 0;
@@ -875,7 +901,7 @@ void trigger_race_checkpoint_verify(entity this)
                                        pl_race_place = p;
                                        if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                                                error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for initially spawning in race) - bailing out"));
-                    }
+                                       }
                                }
                        }
                }
@@ -886,7 +912,7 @@ void trigger_race_checkpoint_verify(entity this)
                pl_race_place = race_lowest_place_spawn;
                if (!Spawn_FilterOutBadSpots(this, findchain(classname, "info_player_deathmatch"), 0, false, true)) {
                        error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(pl_race_place), " (used for qualifying) - bailing out"));
-        }
+               }
        } else {
                pl_race_checkpoint = race_NextCheckpoint(0);
                g_race_qualifying = 1;
@@ -908,8 +934,8 @@ void trigger_race_checkpoint_verify(entity this)
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (argv(0) == cp.targetname) {
                                                cp.race_checkpoint = stof(argv(1));
-                    }
-                }
+                                       }
+                               }
                        }
                        fclose(fh);
                }
@@ -917,37 +943,12 @@ void trigger_race_checkpoint_verify(entity this)
 
        g_race_qualifying = qual;
 
-       IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
-       {
-               if(it.targetname == "" || !it.targetname) // somehow this is a case...
-                       continue;
-               entity cpt = it;
-               FOREACH_ENTITY_STRING(target, cpt.targetname,
-               {
-                       vector org = (it.absmin + it.absmax) * 0.5;
-                       if(cpt.race_checkpoint == 0)
-                               WaypointSprite_SpawnFixed(WP_RaceStart, org, it, sprite, RADARICON_NONE);
-                       else
-                               WaypointSprite_SpawnFixed(WP_RaceCheckpoint, org, it, sprite, RADARICON_NONE);
-
-                       it.sprite.realowner = cpt;
-                       it.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
-               });
-       });
-
        if (race_timed_checkpoint) {
                if (defrag_ents) {
                        IL_EACH(g_race_targets, it.classname == "target_checkpoint" || it.classname == "target_startTimer" || it.classname == "target_stopTimer",
                        {
-                               entity cpt = it;
-                               if(it.classname == "target_startTimer" || it.classname == "target_stopTimer") {
-                                       if(it.targetname == "" || !it.targetname) // somehow this is a case...
-                                               continue;
-                                       FOREACH_ENTITY_STRING(target, cpt.targetname, {
-                                               if(it.sprite)
-                                                       WaypointSprite_UpdateSprites(it.sprite, ((cpt.classname == "target_startTimer") ? WP_RaceStart : WP_RaceFinish), WP_Null, WP_Null);
-                                       });
-                               }
+                               defrag_waypointsprites(it, it);
+
                                if(it.classname == "target_checkpoint") {
                                        if(it.race_checkpoint == -2)
                                                defragcpexists = -1; // something's wrong with the defrag cp file or it has not been written yet, set defragcpexists to -1 so that it will be rewritten when someone finishes
@@ -958,17 +959,17 @@ void trigger_race_checkpoint_verify(entity this)
                                for (entity cp = NULL; (cp = find(cp, classname, "target_checkpoint"));) {
                                        if (cp.race_checkpoint > largest_cp_id) {
                                                largest_cp_id = cp.race_checkpoint;
-                    }
-                }
+                                       }
+                               }
                                for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
                                        cp.race_checkpoint = largest_cp_id + 1; // finish line
-                }
+                               }
                                race_highest_checkpoint = largest_cp_id + 1;
                                race_timed_checkpoint = largest_cp_id + 1;
                        } else {
                                for (entity cp = NULL; (cp = find(cp, classname, "target_stopTimer"));) {
                                        cp.race_checkpoint = 255; // finish line
-                }
+                               }
                                race_highest_checkpoint = 255;
                                race_timed_checkpoint = 255;
                        }
@@ -977,14 +978,14 @@ void trigger_race_checkpoint_verify(entity this)
                        {
                                if (it.race_checkpoint == 0) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null);
-                } else if (it.race_checkpoint == race_timed_checkpoint) {
+                               } else if (it.race_checkpoint == race_timed_checkpoint) {
                                        WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null);
                                }
-            });
+                       });
                }
        }
 
-       if (defrag_ents) {
+       if (defrag_ents) { /* The following hack shall be removed when per-player trigger_multiple.wait is implemented for cts */
                for (entity trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) {
                        for (entity targ = NULL; (targ = find(targ, targetname, trigger.target)); ) {
                                if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") {
index 11509b5428a74f26d96fdc29c21bdfb7019317e5..c26a0b16550ac51062a40c742468d5f93f214076 100644 (file)
@@ -28,8 +28,6 @@ enum
 /// \brief Indicates that the player is not allowed to join a team.
 const int TEAM_NOT_ALLOWED = -1;
 
-.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
-
 .int m_team_balance_state; ///< Holds the state of the team balance entity.
 .entity m_team_balance_team[NUM_TEAMS]; ///< ???
 
index 5cce9758dfb63dc44e0c798033ffa5aaf51ad965..06787c6ffa06c72ec593b4429560949c99e74996 100644 (file)
@@ -12,6 +12,8 @@ string autocvar_g_forced_team_otherwise;
 
 bool lockteams;
 
+.int team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
+
 // ========================== Global teams API ================================
 
 void Team_InitTeams();
index 954bcc46c7da86874ea74996499745898e0f6c68..810cc2638453561cbf11ab56f2ecfd591310db5c 100644 (file)
@@ -110,15 +110,24 @@ void weapon_defaultspawnfunc(entity this, Weapon wpn)
        // if we don't already have ammo, give us some ammo
        if ((wpn.ammo_type != RES_NONE) && !GetResource(this, wpn.ammo_type))
        {
-               switch (wpn.ammo_type)
+               int ammo = 0;
+               if (q3compat && this.count > 0)
+                       ammo = this.count * GetAmmoConsumptionQ3(wpn.netname);
+                       // WEAPONTODO: magazines of MG, rifle and OK weapons are unaccounted for
+               else
                {
-                       case RES_SHELLS:  SetResource(this, wpn.ammo_type, cvar("g_pickup_shells_weapon"));  break;
-                       case RES_BULLETS: SetResource(this, wpn.ammo_type, cvar("g_pickup_nails_weapon"));   break;
-                       case RES_ROCKETS: SetResource(this, wpn.ammo_type, cvar("g_pickup_rockets_weapon")); break;
-                       case RES_CELLS:   SetResource(this, wpn.ammo_type, cvar("g_pickup_cells_weapon"));   break;
-                       case RES_PLASMA:  SetResource(this, wpn.ammo_type, cvar("g_pickup_plasma_weapon"));  break;
-                       case RES_FUEL:    SetResource(this, wpn.ammo_type, cvar("g_pickup_fuel_weapon"));    break;
+                       switch (wpn.ammo_type)
+                       {
+                               case RES_SHELLS:  ammo = cvar("g_pickup_shells_weapon");  break;
+                               case RES_BULLETS: ammo = cvar("g_pickup_nails_weapon");   break;
+                               case RES_ROCKETS: ammo = cvar("g_pickup_rockets_weapon"); break;
+                               case RES_CELLS:   ammo = cvar("g_pickup_cells_weapon");   break;
+                               case RES_PLASMA:  ammo = cvar("g_pickup_plasma_weapon");  break;
+                               case RES_FUEL:    ammo = cvar("g_pickup_fuel_weapon");    break;
+                       }
                }
+
+               SetResource(this, wpn.ammo_type, ammo);
        }
 
        #if 0 // WEAPONTODO
index 6c397d05353850515f0b2af7ce2870ef1a1875fb..02c059ea808c0d00ab4c3113c5fccb9e1249990a 100644 (file)
@@ -887,11 +887,8 @@ spawnfunc(worldspawn)
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
 
-       if(fexists(strcat("scripts/", mapname, ".arena")))
-               cvar_settemp("sv_q3acompat_machineshotgunswap", "1");
-
-       if(fexists(strcat("scripts/", mapname, ".defi")))
-               cvar_settemp("sv_q3defragcompat", "1");
+       q3compat = BITSET(q3compat, Q3COMPAT_ARENA, fexists(strcat("scripts/", mapname, ".arena")));
+       q3compat = BITSET(q3compat, Q3COMPAT_DEFI, fexists(strcat("scripts/", mapname, ".defi")));
 
        if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
        {
index a6213cb24a48b6b13ce159ede2f57592c8d416f1..1634c256cca1102ea23f05da17e097a0b37bc016 100644 (file)
@@ -499,8 +499,7 @@ sv_gameplayfix_consistentplayerprethink 1
 sv_gameplayfix_gravityunaffectedbyticrate 1
 sv_gameplayfix_nogravityonground 1
 
-set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)"
-set sv_q3defragcompat 0 "toggle for some compatibility hacks (for Q3DF map compatibility)"
+set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)
 
 set g_movement_highspeed 1 "multiplier scale for movement speed (applies to sv_maxspeed and sv_maxairspeed, also applies to air acceleration when g_movement_highspeed_q3_compat is set to 0)"
 set g_movement_highspeed_q3_compat 0 "apply speed modifiers to air movement in a more Q3-compatible way (only apply speed buffs and g_movement_highspeed to max air speed, not to acceleration)"