]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merged Lyberta/GivePlayerAmmo into Lyberta/RandomStartWeapons.
authorLyberta <lyberta@lyberta.net>
Sat, 26 Aug 2017 23:09:41 +0000 (02:09 +0300)
committerLyberta <lyberta@lyberta.net>
Sat, 26 Aug 2017 23:09:41 +0000 (02:09 +0300)
27 files changed:
.gitlab-ci.yml
commands.cfg
defaultClient.cfg
gamemodes-server.cfg
qcsrc/client/hud/panel/scoreboard.qc
qcsrc/client/main.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/mapinfo.qh
qcsrc/common/monsters/sv_spawn.qc
qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/vampire/sv_vampire.qc
qcsrc/common/t_items.qc
qcsrc/common/t_items.qh
qcsrc/common/weapons/weapon.qh
qcsrc/common/weapons/weapon/rifle.qh
qcsrc/lib/_all.inc
qcsrc/server/bot/default/aim.qc
qcsrc/server/bot/default/bot.qc
qcsrc/server/client.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/mutator/gamemode_assault.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qh
qcsrc/server/weapons/common.qc
qcsrc/server/weapons/tracing.qc
scripts/ok_nade_counter.shader

index 5427860963c0ccbf2c1760a8f49e6d75497d7acc..5d525a4c5773a647cd82ee8e27bd338b927153f0 100644 (file)
@@ -29,7 +29,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
     - make
-    - EXPECT=585cfa6d62ce59f4854bedfce7c51c20
+    - EXPECT=662ab75eeb91d25c2d86ebb81ce8dc02
     - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
       | tee /dev/stderr
       | grep '^:'
index 93b68f901e57154ec7512a2d98830fce7ec168ad..66ca90e2aa25dcb0aab5f659ba07b2c722b2e44b 100644 (file)
@@ -185,6 +185,7 @@ alias skinmob "editmob skin ${* ?}"
 alias namemob "editmob name ${* ?}"
 alias movemob "editmob movetarget ${* ?}"
 alias butcher "editmob butcher ${* ?}"
+alias mobbutcher "editmob butcher ${* ?}"
 
 
 // ============================================================
@@ -209,7 +210,6 @@ alias gettaginfo           "qc_cmd_sv     gettaginfo           ${* ?}" // Get sp
 alias gotomap              "qc_cmd_sv     gotomap              ${* ?}" // Simple command to switch to another map
 alias lockteams            "qc_cmd_sv     lockteams            ${* ?}" // Disable the ability for players to switch or enter teams
 alias make_mapinfo         "qc_cmd_sv     make_mapinfo         ${* ?}" // Automatically rebuild mapinfo files
-alias mobbutcher           "qc_cmd_sv     mobbutcher           ${* ?}" // Remove all monsters on the map
 alias moveplayer           "qc_cmd_sv     moveplayer           ${* ?}" // Change the team/status of a player
 alias nospectators         "qc_cmd_sv     nospectators         ${* ?}" // Automatically remove spectators from a match
 alias playerdemo           "qc_cmd_sv     playerdemo           ${* ?}" // Control the ability to save demos of players
index 7aa1ad5cf07afc03f9d6fb879693ea649ee4789a..c12f69feeb86b881f59ebff7d52fda05d4445946 100644 (file)
@@ -269,7 +269,7 @@ r_shadow_realtime_world_lightmaps 1
 r_shadow_realtime_world_importlightentitiesfrommap 0 // Whether build process uses keepLights is nontransparent and may change, so better make keepLights not matter.
 cl_decals_fadetime 5
 cl_decals_time 1
-seta cl_gunalign 3 "Gun alignment; 1 = center (if allowed by g_shootfromclient) or right, 2 = center (if allowed by g_shootfromclient) or left, 3 = right only, 4 = left only"
+seta cl_gunalign 3 "Gun alignment; 1 = center, 3 = right, 4 = left; requires reconnect"
 seta cl_nogibs 0 "reduce number of violence effects, or remove them totally"
 seta cl_particlegibs 0 "simpler gibs"
 seta cl_gibs_damageforcescale 3.5 "force to push around gibs"
index 684edb3a83c64c73776554c649dcbdc3d5e0ea8b..6790a3b4fb9575ab91e070c57bc984ac97609ba2 100644 (file)
@@ -256,9 +256,9 @@ set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this vel
 set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!"
 set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag"
 set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
-set g_ctf_throw_punish_count 2
+set g_ctf_throw_punish_count 3
 set g_ctf_throw_punish_delay 30
-set g_ctf_throw_punish_time 5
+set g_ctf_throw_punish_time 10
 set g_ctf_throw_strengthmultiplier 2 "multiplier for velocity when you have the strength... essentially, throw the flag REALLY hard when you have the strength :D"
 set g_ctf_throw_velocity_forward 500 "how fast or far a player can throw the flag"
 set g_ctf_throw_velocity_up 200 "upwards velocity added upon initial throw"
@@ -521,3 +521,4 @@ set g_invasion_spawn_delay 0.25
 set g_invasion_spawnpoint_spawn_delay 0.5
 set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
 set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
+set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)"
index 34ecd834f7ec682ca1e6c1c00d9f8164928e783a..3a11bdb1e7f0085aba298ff439644428dfc7e253 100644 (file)
@@ -153,13 +153,12 @@ float SetTeam(entity pl, float Team);
 //float lastpnum;
 void Scoreboard_UpdatePlayerTeams()
 {
-       float Team;
        entity pl, tmp;
        //int num = 0;
        for(pl = players.sort_next; pl; pl = pl.sort_next)
        {
                //num += 1;
-               Team = entcs_GetScoreTeam(pl.sv_entnum);
+               int Team = entcs_GetScoreTeam(pl.sv_entnum);
                if(SetTeam(pl, Team))
                {
                        tmp = pl.sort_prev;
index 323ebb3516147bce98cc49f0d8286c2ec85324b1..7ad10879f7ac23b05a69a96ae7d4f7441ea51c2d 100644 (file)
@@ -108,6 +108,11 @@ void CSQC_Init()
                maxclients = i;
        }
 
+       // needs to be done so early because of the constants they create
+       static_init();
+       static_init_late();
+       static_init_precache();
+
        binddb = db_create();
        tempdb = db_create();
        ClientProgsDB = db_load("client.db");
@@ -147,6 +152,9 @@ void CSQC_Init()
 
        GetTeam(NUM_SPECTATOR, true); // add specs first
 
+       for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
+               weapon_accuracy[w] = -1;
+
        // precaches
 
        if(autocvar_cl_reticle)
@@ -488,9 +496,9 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
 
     int f = ReadByte();
 
-       scoreboard_showscores_force = (f & 1);
+       scoreboard_showscores_force = (f & BIT(0));
 
-       if(f & 2)
+       if(f & BIT(1))
        {
                newspectatee_status = ReadByte();
                if(newspectatee_status == player_localnum + 1)
@@ -499,9 +507,9 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew)
        else
                newspectatee_status = 0;
 
-       spectatorbutton_zoom = (f & 4);
+       spectatorbutton_zoom = (f & BIT(2));
 
-       if(f & 16)
+       if(f & BIT(4))
        {
                num_spectators = ReadByte();
 
index 223a81f7c4d4dfb6039a3ea6b2189b0df864dfd0..9992a37f4ce032faa7f25549728ff5d3303846a2 100644 (file)
@@ -1377,8 +1377,8 @@ void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale
                        best.cnt += 1;
 
                        this.havocbot_attack_time = 0;
-                       if(checkpvs(this.view_ofs,cp))
-                       if(checkpvs(this.view_ofs,best))
+                       if(checkpvs(this.origin + this.view_ofs, cp))
+                       if(checkpvs(this.origin + this.view_ofs, best))
                                this.havocbot_attack_time = time + 2;
                }
                else
@@ -1448,8 +1448,8 @@ bool havocbot_goalrating_ons_generator_attack(entity this, float ratingscale)
                        bestwp.cnt += 1;
 
                        this.havocbot_attack_time = 0;
-                       if(checkpvs(this.view_ofs,g))
-                       if(checkpvs(this.view_ofs,bestwp))
+                       if(checkpvs(this.origin + this.view_ofs, g))
+                       if(checkpvs(this.origin + this.view_ofs, bestwp))
                                this.havocbot_attack_time = time + 5;
 
                        return true;
index 4c0fd782904bab4993f0839d428210bb42b2746d..2dd84596e46991e8f3f75daeb63b524273825d01 100644 (file)
@@ -458,7 +458,7 @@ REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway));
 CLASS(Invasion, Gametype)
     INIT(Invasion)
     {
-        this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0",_("Survive against waves of monsters"));
+        this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters"));
     }
     METHOD(Invasion, m_parse_mapinfo, bool(string k, string v))
     {
@@ -466,6 +466,9 @@ CLASS(Invasion, Gametype)
             case "teams":
                 cvar_set("g_invasion_teams", v);
                 return true;
+            case "type":
+                cvar_set("g_invasion_type", v);
+                return true;
         }
         return false;
     }
index 4bbe2ce3d8f8a94fc336a0165e8009d0c16b0767..d6989ad31839d52229cc8f612879fd6c83ab8686 100644 (file)
@@ -16,11 +16,12 @@ entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby,
        //if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; }
 
        setorigin(e, orig);
+       bool allow_any = boolean(monster == "anyrandom");
 
-       if(monster == "random")
+       if(monster == "random" || allow_any)
        {
                RandomSelection_Init();
-               FOREACH(Monsters, it != MON_Null && !(it.spawnflags & MONSTER_TYPE_PASSIVE) && !(it.spawnflags & MON_FLAG_HIDDEN),
+               FOREACH(Monsters, it != MON_Null && (allow_any || (!(it.spawnflags & MONSTER_TYPE_PASSIVE) && !(it.spawnflags & MON_FLAG_HIDDEN))),
                {
                        RandomSelection_AddEnt(it, 1, 1);
                });
index 0e170e57ec6249fd4e3ed9e82a284145f691a078..4a18cc930879ad34e28ef7551c7af1e381bf8d46 100644 (file)
@@ -1,13 +1,13 @@
 #include "sv_damagetext.qh"
 
-AUTOCVAR(sv_damagetext, int, 2, "<= 0: disabled, >= 1: spectators, >= 2: players, >= 3: all players");
+AUTOCVAR(sv_damagetext, int, 2, "<= 0: disabled, >= 1: visible to spectators, >= 2: visible to attacker, >= 3: all players see everyone's damage");
 
 REGISTER_MUTATOR(damagetext, true);
 
-#define SV_DAMAGETEXT_DISABLED()        (autocvar_sv_damagetext <= 0 /* disabled */)
-#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1 /* spectators only */)
-#define SV_DAMAGETEXT_PLAYERS()         (autocvar_sv_damagetext >= 2 /* players */)
-#define SV_DAMAGETEXT_ALL()             (autocvar_sv_damagetext >= 3 /* all players */)
+#define SV_DAMAGETEXT_DISABLED()        (autocvar_sv_damagetext <= 0 || autocvar_g_instagib)
+#define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1)
+#define SV_DAMAGETEXT_PLAYERS()         (autocvar_sv_damagetext >= 2)
+#define SV_DAMAGETEXT_ALL()             (autocvar_sv_damagetext >= 3)
 MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) {
     if (SV_DAMAGETEXT_DISABLED()) return;
     const entity attacker = M_ARGV(0, entity);
index 090ff82a362f22c7555f851b4c3477ebcfac18fa..ae1bbff6525cac70a7348eb46bc4049ae81c76a7 100644 (file)
@@ -1481,11 +1481,6 @@ MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString)
        M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades");
 }
 
-MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString)
-{
-       M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Nades");
-}
-
 MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString)
 {
        M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3nades^8 are enabled, press 'g' to use them\n");
index 3a435c5ed1dea13ad797e4097e1a79c749b319cf..92c5943c3d75455f7bc3d41b6f95184df7ba8555 100644 (file)
@@ -12,8 +12,7 @@ MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
        if(frag_target != frag_attacker)
        if(!IS_DEAD(frag_target))
        {
-               frag_attacker.health += bound(0, damage_take, frag_target.health);
-               frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit);
+               GivePlayerHealth(frag_attacker, bound(0, damage_take, frag_target.health));
        }
 }
 
index 4c0125afb687c923ac92e1fcd068232e03e19482..79552505ad961f0267958cc7533b3b7485e4662e 100644 (file)
@@ -648,33 +648,80 @@ void Item_ScheduleInitialRespawn(entity e)
        Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : ITEM_RESPAWNTIME_INITIAL(e)));
 }
 
-void GivePlayerHealth(entity player, float amount)
+void GivePlayerResource(entity player, .float resource_type, float amount)
 {
        if (amount == 0)
        {
                return;
        }
-       player.health = bound(player.health, player.health + amount,
-                g_pickup_healthmega_max);
-       player.pauserothealth_finished = max(player.pauserothealth_finished, time +
-               autocvar_g_balance_pause_health_rot);
+       switch (resource_type)
+       {
+               case health:
+               {
+                       // Ugly hack. We do not check if health goes beyond hard limit since
+                       // currently it is done in player_regen. We need to bring back this
+                       // check when other code is ported to this function.
+                       player.health = bound(player.health, player.health + amount,
+                               autocvar_g_balance_health_limit);
+                       // Correct code:
+                       //player.health = bound(player.health, player.health + amount,
+                       //      min(autocvar_g_balance_health_limit, ITEM_COUNT_HARD_LIMIT));
+                       player.pauserothealth_finished = max(player.pauserothealth_finished,
+                               time + autocvar_g_balance_pause_health_rot);
+                       return;
+               }
+               case armorvalue:
+               {
+                       // Ugly hack. We do not check if armor goes beyond hard limit since
+                       // currently it is done in player_regen. We need to bring back this
+                       // check when other code is ported to this function.
+                       player.armorvalue = bound(player.armorvalue, player.armorvalue +
+                               amount, autocvar_g_balance_armor_limit);
+                       // Correct code:
+                       //player.armorvalue = bound(player.armorvalue, player.armorvalue +
+                       //      amount, min(autocvar_g_balance_armor_limit,
+                       //      ITEM_COUNT_HARD_LIMIT));
+                       player.pauserotarmor_finished = max(player.pauserotarmor_finished,
+                               time + autocvar_g_balance_pause_armor_rot);
+                       return;
+               }
+               case ammo_shells:
+               case ammo_nails:
+               case ammo_rockets:
+               case ammo_cells:
+               case ammo_plasma:
+               {
+                       GivePlayerAmmo(player, resource_type, amount);
+                       return;
+               }
+               case ammo_fuel:
+               {
+                       player.ammo_fuel = bound(player.ammo_fuel, player.ammo_fuel +
+                               amount, min(g_pickup_fuel_max, ITEM_COUNT_HARD_LIMIT));
+                       player.pauserotfuel_finished = max(player.pauserotfuel_finished,
+                               time + autocvar_g_balance_pause_fuel_rot);
+                       return;
+               }
+       }
+}
+
+void GivePlayerHealth(entity player, float amount)
+{
+       GivePlayerResource(player, health, amount);
 }
 
 void GivePlayerArmor(entity player, float amount)
 {
-       if (amount == 0)
-       {
-               return;
-       }
-       player.armorvalue = bound(player.armorvalue, player.armorvalue + amount,
-                g_pickup_armormega_max);
-       player.pauserotarmor_finished = max(player.pauserotarmor_finished, time +
-               autocvar_g_balance_pause_armor_rot);
+       GivePlayerResource(player, armorvalue, amount);
 }
 
 void GivePlayerAmmo(entity player, .float ammotype, float amount)
 {
-       float maxvalue = 999;
+       if (amount == 0)
+       {
+               return;
+       }
+       float maxvalue = ITEM_COUNT_HARD_LIMIT;
        switch (ammotype)
        {
                case ammo_shells:
@@ -702,13 +749,14 @@ void GivePlayerAmmo(entity player, .float ammotype, float amount)
                        maxvalue = g_pickup_nails_max;
                        break;
                }
-               case ammo_fuel:
-               {
-                       maxvalue = g_pickup_fuel_max;
-                       break;
-               }
        }
-       player.(ammotype) = min(player.(ammotype) + amount, maxvalue);
+       player.(ammotype) = min(player.(ammotype) + amount,
+               min(maxvalue, ITEM_COUNT_HARD_LIMIT));
+}
+
+void GivePlayerFuel(entity player, float amount)
+{
+       GivePlayerResource(player, ammo_fuel, amount);
 }
 
 void GivePlayerRandomWeapons(entity player, int num_weapons,
@@ -792,7 +840,7 @@ void GivePlayerRandomWeapons(entity player, int num_weapons,
        }
 }
 
-float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax, float mode)
+float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax)
 {
        if (!item.(ammotype))
                return false;
@@ -801,8 +849,13 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax
        {
                if ((player.(ammotype) < ammomax) || item.pickup_anyway > 0)
                {
-                       player.(ammotype) = bound(player.(ammotype), ammomax, player.(ammotype) + item.(ammotype));
-                       goto YEAH;
+                       float amount = item.(ammotype);
+                       if ((player.(ammotype) + amount) > ammomax)
+                       {
+                               amount = ammomax - player.(ammotype);
+                       }
+                       GivePlayerResource(player, ammotype, amount);
+                       return true;
                }
        }
        else if(g_weapon_stay == 2)
@@ -810,29 +863,11 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax
                float mi = min(item.(ammotype), ammomax);
                if (player.(ammotype) < mi)
                {
-                       player.(ammotype) = mi;
-                       goto YEAH;
+                       GivePlayerResource(player, ammotype, mi - player.(ammotype));
                }
+               return true;
        }
-
        return false;
-
-LABEL(YEAH)
-       switch(mode)
-       {
-               case ITEM_MODE_FUEL:
-                       player.pauserotfuel_finished = max(player.pauserotfuel_finished, time + autocvar_g_balance_pause_fuel_rot);
-                       break;
-               case ITEM_MODE_HEALTH:
-                       player.pauserothealth_finished = max(player.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
-                       break;
-               case ITEM_MODE_ARMOR:
-                       player.pauserotarmor_finished = max(player.pauserotarmor_finished, time + autocvar_g_balance_pause_armor_rot);
-                       break;
-               default:
-                       break;
-       }
-       return true;
 }
 
 float Item_GiveTo(entity item, entity player)
@@ -861,16 +896,14 @@ float Item_GiveTo(entity item, entity player)
                        }
                }
        }
-
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max, ITEM_MODE_NONE);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_nails, g_pickup_nails_max, ITEM_MODE_NONE);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max, ITEM_MODE_NONE);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max, ITEM_MODE_NONE);
-       pickedup |= Item_GiveAmmoTo(item, player, ammo_plasma, g_pickup_plasma_max, ITEM_MODE_NONE);
-       pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH);
-       pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR);
-
+       pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health);
+       pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_shells, g_pickup_shells_max);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_nails, g_pickup_nails_max);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_plasma, g_pickup_plasma_max);
+       pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max);
        if (item.itemdef.instanceOfWeaponPickup)
        {
                WepSet w;
index 009fc926a76529812238fe448e77ececd8acf959..af7d91758dd4afd0bbbfa69fc8fc155433f71609 100644 (file)
@@ -4,6 +4,9 @@
 #include <server/defs.qh>
 #endif
 
+/// \brief Unconditional maximum amount of items the player can have.
+const int ITEM_COUNT_HARD_LIMIT = 999;
+
 const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 
 // item networking
@@ -86,6 +89,13 @@ void Item_ScheduleRespawn(entity e);
 
 void Item_ScheduleInitialRespawn(entity e);
 
+/// \brief Gives player a resource such as health, armor or ammo.
+/// \param[in,out] player Player to give resource to.
+/// \param[in] resource_type Type of the resource.
+/// \param[in] amount Amount of resource to give.
+/// \return No return.
+void GivePlayerResource(entity player, .float resource_type, float amount);
+
 /// \brief Gives health to the player.
 /// \param[in,out] player Player to give health to.
 /// \param[in] amount Amount of health to give.
@@ -105,6 +115,12 @@ void GivePlayerArmor(entity player, float amount);
 /// \return No return.
 void GivePlayerAmmo(entity player, .float ammotype, float amount);
 
+/// \brief Gives fuel to the player.
+/// \param[in,out] player Player to give fuel to.
+/// \param[in] amount Amount of fuel to give.
+/// \return No return.
+void GivePlayerFuel(entity player, float amount);
+
 /// \brief Give several random weapons and ammo to the player.
 /// \param[in,out] player Player to give weapons to.
 /// \param[in] num_weapons Number of weapons to give.
@@ -119,11 +135,7 @@ void GivePlayerRandomWeapons(entity player, int num_weapons,
        string weapon_names, float shells, float bullets, float rockets,
        float cells, float plasma);
 
-float ITEM_MODE_NONE = 0;
-float ITEM_MODE_HEALTH = 1;
-float ITEM_MODE_ARMOR = 2;
-float ITEM_MODE_FUEL = 3;
-float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax, float mode);
+float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax);
 
 float Item_GiveTo(entity item, entity player);
 
index ea2e2e1730546bc7686b65b55172063a04b00f1a..4be22446bf9dae2f8a314138be4e4b4fd4909426 100644 (file)
@@ -198,6 +198,7 @@ const int WEP_TYPE_MELEE_PRI      = 0x400; // primary attack is melee swing (for
 const int WEP_TYPE_MELEE_SEC      = 0x800; // secondary attack is melee swing (for animation)
 const int WEP_FLAG_DUALWIELD      = 0x1000; // weapon can be dual wielded
 const int WEP_FLAG_NODUAL         = 0x2000; // weapon doesn't work well with dual wielding (fireball etc just explode on fire), doesn't currently prevent anything
+const int WEP_FLAG_PENETRATEWALLS = 0x4000; // weapon has high calibur bullets that can penetrate thick walls (WEAPONTODO)
 
 // variables:
 string weaponorder_byid;
index 29520f5455bcbdb36d69b6dc8586fba85dfd3ffc..ed5496f050f28cb841949a0937ebd4a279c589d6 100644 (file)
@@ -3,7 +3,7 @@
 CLASS(Rifle, Weapon)
 /* ammotype  */ ATTRIB(Rifle, ammo_field, .int, ammo_nails);
 /* impulse   */ ATTRIB(Rifle, impulse, int, 7);
-/* flags     */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* flags     */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
 /* rating    */ ATTRIB(Rifle, bot_pickupbasevalue, float, 7000);
 /* color     */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0');
 /* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle");
index d8f07455a28c7766065c6b6e0737020a4758a5e4..4da78f1444eb27b397d926442c7a153012adf01e 100644 (file)
@@ -250,13 +250,7 @@ void make_safe_for_remove(entity this);
 
 #ifdef CSQC
        void _CSQC_Init();
-       void CSQC_Init()
-       {
-               static_init();
-               static_init_late();
-               static_init_precache();
-               if (_CSQC_Init) _CSQC_Init();
-       }
+       void CSQC_Init() { if (_CSQC_Init) _CSQC_Init(); }
        #define CSQC_Init _CSQC_Init
 
        void _CSQC_Shutdown();
index feb19afe1f921b25fec4c0422555482c70ff1b43..8f2abb3f824b5745448a5fb4c16ab21545e5451b 100644 (file)
@@ -307,7 +307,7 @@ float bot_aimdir(entity this, vector v, float maxfiredeviation)
        //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n");
 
        // decide whether to fire this time
-       if ((normalize(v) * shotdir) >= cos(maxfiredeviation * DEG2RAD))
+       if (v * shotdir >= cos(maxfiredeviation * DEG2RAD))
        if(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1))
                this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5);
        //traceline(shotorg,shotorg+shotdir*1000,false,NULL);
index 6cb24f0be03581e50e8f0fcb7d603b9376a2eb41..5bc86b84e14ca350c18d75493500cf7ca55da63a 100644 (file)
@@ -630,8 +630,7 @@ float bot_fixcount()
        // only add one bot per frame to avoid utter chaos
        if(time > botframe_nextthink)
        {
-               //dprint(ftos(bots), " ? ", ftos(currentbots), "\n");
-               while (currentbots < bots)
+               if (currentbots < bots)
                {
                        if (bot_spawn() == NULL)
                        {
index 5cc8349acc6cb2fbec530d16d368d92386ab1dab..bb06092cc25462ee9d1bc58d31ef01e7200a143b 100644 (file)
@@ -113,20 +113,18 @@ bool ClientData_Send(entity this, entity to, int sf)
        if (IS_SPEC(e)) e = e.enemy;
 
        sf = 0;
-       if (CS(e).race_completed)       sf |= 1; // forced scoreboard
-       if (CS(to).spectatee_status)    sf |= 2; // spectator ent number follows
-       if (CS(e).zoomstate)            sf |= 4; // zoomed
-       if (autocvar_sv_showspectators) sf |= 16; // show spectators
+       if (CS(e).race_completed)       sf |= BIT(0); // forced scoreboard
+       if (CS(to).spectatee_status)    sf |= BIT(1); // spectator ent number follows
+       if (CS(e).zoomstate)            sf |= BIT(2); // zoomed
+       if (autocvar_sv_showspectators) sf |= BIT(4); // show spectators
 
        WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA);
        WriteByte(MSG_ENTITY, sf);
 
-       if (sf & 2)
-       {
+       if (sf & BIT(1))
                WriteByte(MSG_ENTITY, CS(to).spectatee_status);
-       }
 
-       if(sf & 16)
+       if(sf & BIT(4))
        {
                float specs = CountSpectators(e, to);
                WriteByte(MSG_ENTITY, specs);
@@ -1626,7 +1624,6 @@ void player_regen(entity this)
        regen_health_stable = M_ARGV(9, float);
        regen_health_rotstable = M_ARGV(10, float);
 
-
        if(!mutator_returnvalue)
        if(!STAT(FROZEN, this))
        {
@@ -1665,6 +1662,18 @@ void player_regen(entity this)
 
                this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf);
        }
+       // Ugly hack to make sure the haelth and armor don't go beyond hard limit.
+       // TODO: Remove this hack when all code uses GivePlayerHealth and
+       // GivePlayerArmor.
+       if (this.health > ITEM_COUNT_HARD_LIMIT)
+       {
+               this.health = ITEM_COUNT_HARD_LIMIT;
+       }
+       if (this.armorvalue > ITEM_COUNT_HARD_LIMIT)
+       {
+               this.armorvalue = ITEM_COUNT_HARD_LIMIT;
+       }
+       // End hack.
 }
 
 bool zoomstate_set;
index 2812da09d08cd5b6e26a3c9407bc43d17bdf3a3b..4a851b18d08f76b25f2b73b00c5f449185f6352c 100644 (file)
@@ -272,6 +272,7 @@ void cvar_changes_init()
                BADCVAR("g_freezetag");
                BADCVAR("g_freezetag_teams");
                BADCVAR("g_invasion_teams");
+               BADCVAR("g_invasion_type");
                BADCVAR("g_jailbreak");
                BADCVAR("g_jailbreak_teams");
                BADCVAR("g_keepaway");
index 85801af52a8fc900db8fcff339c2b6b153c5226d..bd88131d31b2ddfa6e6e2a81aac0e55c63a7ec37 100644 (file)
@@ -429,8 +429,8 @@ void havocbot_goalrating_ast_targets(entity this, float ratingscale)
 
                        this.havocbot_attack_time = 0;
 
-                       if(checkpvs(this.view_ofs,it))
-                       if(checkpvs(this.view_ofs,best))
+                       if(checkpvs(this.origin + this.view_ofs, it))
+                       if(checkpvs(this.origin + this.view_ofs, best))
                        {
                        //      dprint("increasing attack time for this target\n");
                                this.havocbot_attack_time = time + 2;
index fabcc26f62cb26bef62edda59104324330421a82..8ec353c1dae376e1060d6978835c09517bf24ff9 100644 (file)
@@ -5,6 +5,9 @@
 
 #include <server/teamplay.qh>
 
+IntrusiveList g_invasion_roundends;
+STATIC_INIT(g_invasion_roundends) { g_invasion_roundends = IL_NEW(); }
+
 IntrusiveList g_invasion_waves;
 STATIC_INIT(g_invasion_waves) { g_invasion_waves = IL_NEW(); }
 
@@ -18,10 +21,46 @@ int autocvar_g_invasion_monster_count;
 bool autocvar_g_invasion_zombies_only;
 float autocvar_g_invasion_spawn_delay;
 
+bool victent_present;
+.bool inv_endreached;
+
 bool inv_warning_shown; // spammy
 
 .string spawnmob;
 
+void target_invasion_roundend_use(entity this, entity actor, entity trigger)
+{
+       if(!IS_PLAYER(actor)) { return; }
+
+       actor.inv_endreached = true;
+
+       int plnum = 0;
+       int realplnum = 0;
+       // let's not count bots
+       FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
+               ++realplnum;
+               if(it.inv_endreached)
+                       ++plnum;
+       });
+       if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players
+               return;
+
+       this.winning = true;
+}
+
+spawnfunc(target_invasion_roundend)
+{
+       if(!g_invasion) { delete(this); return; }
+
+       victent_present = true; // a victory entity is present, we don't need to rely on monster count TODO: merge this with the intrusive list (can check empty)
+
+       if(!this.count) { this.count = 0.7; } // require at least 70% of the players to reach the end before triggering victory
+
+       this.use = target_invasion_roundend_use;
+
+       IL_PUSH(g_invasion_roundends, this);
+}
+
 spawnfunc(invasion_wave)
 {
        if(!g_invasion) { delete(this); return; }
@@ -37,6 +76,60 @@ spawnfunc(invasion_spawnpoint)
        IL_PUSH(g_invasion_spawns, this);
 }
 
+void ClearWinners();
+
+// Invasion stage mode winning condition: If the attackers triggered a round end (by fulfilling all objectives)
+// they win.
+int WinningCondition_Invasion()
+{
+       WinningConditionHelper(NULL); // set worldstatus
+
+       int status = WINNING_NO;
+
+       if(autocvar_g_invasion_type == INV_TYPE_STAGE)
+       {
+               SetWinners(inv_endreached, true);
+
+               int found = 0;
+               IL_EACH(g_invasion_roundends, true,
+               {
+                       ++found;
+                       if(it.winning)
+                       {
+                               bprint("Invasion: round completed.\n");
+                               // winners already set (TODO: teamplay support)
+
+                               status = WINNING_YES;
+                               break;
+                       }
+               });
+
+               if(!found)
+                       status = WINNING_YES; // just end it? TODO: should warn mapper!
+       }
+       else if(autocvar_g_invasion_type == INV_TYPE_HUNT)
+       {
+               ClearWinners();
+
+               int found = 0; // NOTE: this ends the round if no monsters are placed
+               IL_EACH(g_monsters, !(it.spawnflags & MONSTERFLAG_RESPAWNED),
+               {
+                       ++found;
+               });
+
+               if(found <= 0)
+               {
+                       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+                       {
+                               it.winning = true;
+                       });
+                       status = WINNING_YES;
+               }
+       }
+
+       return status;
+}
+
 Monster invasion_PickMonster(int supermonster_count)
 {
        RandomSelection_Init();
@@ -328,8 +421,11 @@ MUTATOR_HOOKFUNCTION(inv, MonsterDies)
 
        if(!(frag_target.spawnflags & MONSTERFLAG_RESPAWNED))
        {
-               inv_numkilled += 1;
-               inv_maxcurrent -= 1;
+               if(autocvar_g_invasion_type == INV_TYPE_ROUND)
+               {
+                       inv_numkilled += 1;
+                       inv_maxcurrent -= 1;
+               }
                if(teamplay) { inv_monsters_perteam[frag_target.team] -= 1; }
 
                if(IS_PLAYER(frag_attacker))
@@ -347,6 +443,10 @@ MUTATOR_HOOKFUNCTION(inv, MonsterDies)
 MUTATOR_HOOKFUNCTION(inv, MonsterSpawn)
 {
        entity mon = M_ARGV(0, entity);
+       mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
+
+       if(autocvar_g_invasion_type == INV_TYPE_HUNT)
+               return false; // allowed
 
        if(!(mon.spawnflags & MONSTERFLAG_SPAWNED))
                return true;
@@ -361,29 +461,20 @@ MUTATOR_HOOKFUNCTION(inv, MonsterSpawn)
 
        if((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, mon.monster_name);
-
-       mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
-}
-
-MUTATOR_HOOKFUNCTION(inv, OnEntityPreSpawn)
-{
-       entity ent = M_ARGV(0, entity);
-
-       // TODO: allow these as "rogues" or something
-       if(startsWith(ent.classname, "monster_"))
-       if(!(ent.spawnflags & MONSTERFLAG_SPAWNED))
-               return true;
 }
 
 MUTATOR_HOOKFUNCTION(inv, SV_StartFrame)
 {
+       if(autocvar_g_invasion_type != INV_TYPE_ROUND)
+               return; // uses map spawned monsters
+
        monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned
        monsters_killed = inv_numkilled;
 }
 
 MUTATOR_HOOKFUNCTION(inv, PlayerRegen)
 {
-       // no regeneration in invasion
+       // no regeneration in invasion, regardless of the game type
        return true;
 }
 
@@ -413,28 +504,6 @@ MUTATOR_HOOKFUNCTION(inv, Damage_Calculate)
        }
 }
 
-MUTATOR_HOOKFUNCTION(inv, SV_ParseClientCommand)
-{
-       if(MUTATOR_RETURNVALUE) // command was already handled?
-               return;
-
-       entity player = M_ARGV(0, entity);
-       string cmd_name = M_ARGV(1, string);
-
-       if(cmd_name == "debuginvasion")
-       {
-               sprint(player, strcat("inv_maxspawned = ", ftos(inv_maxspawned), "\n"));
-               sprint(player, strcat("inv_numspawned = ", ftos(inv_numspawned), "\n"));
-               sprint(player, strcat("inv_numkilled = ", ftos(inv_numkilled), "\n"));
-               sprint(player, strcat("inv_roundcnt = ", ftos(inv_roundcnt), "\n"));
-               sprint(player, strcat("monsters_total = ", ftos(monsters_total), "\n"));
-               sprint(player, strcat("monsters_killed = ", ftos(monsters_killed), "\n"));
-               sprint(player, strcat("inv_monsterskill = ", ftos(inv_monsterskill), "\n"));
-
-               return true;
-       }
-}
-
 MUTATOR_HOOKFUNCTION(inv, BotShouldAttack)
 {
        entity targ = M_ARGV(1, entity);
@@ -445,8 +514,11 @@ MUTATOR_HOOKFUNCTION(inv, BotShouldAttack)
 
 MUTATOR_HOOKFUNCTION(inv, SetStartItems)
 {
-       start_health = 200;
-       start_armorvalue = 200;
+       if(autocvar_g_invasion_type == INV_TYPE_ROUND)
+       {
+               start_health = 200;
+               start_armorvalue = 200;
+       }
 }
 
 MUTATOR_HOOKFUNCTION(inv, AccuracyTargetValid)
@@ -465,6 +537,15 @@ MUTATOR_HOOKFUNCTION(inv, AllowMobSpawning)
        return true;
 }
 
+MUTATOR_HOOKFUNCTION(inv, CheckRules_World)
+{
+       if(autocvar_g_invasion_type == INV_TYPE_ROUND)
+               return false;
+
+       M_ARGV(0, float) = WinningCondition_Invasion();
+       return true;
+}
+
 MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
 {
        M_ARGV(0, float) = invasion_teams;
@@ -487,6 +568,9 @@ void invasion_ScoreRules(int inv_teams)
 
 void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
 {
+       if(autocvar_g_invasion_type == INV_TYPE_HUNT || autocvar_g_invasion_type == INV_TYPE_STAGE)
+               cvar_set("fraglimit", "0");
+
        if(autocvar_g_invasion_teams)
        {
                invasion_teams = bound(2, autocvar_g_invasion_teams, 4);
@@ -507,11 +591,14 @@ void invasion_DelayedInit(entity this) // Do this check with a delay so we can w
 
        independent_players = 0;
 
-       round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
-       round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
+       if(autocvar_g_invasion_type == INV_TYPE_ROUND)
+       {
+               round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
+               round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
 
-       inv_roundcnt = 0;
-       inv_maxrounds = 15; // 15?
+               inv_roundcnt = 0;
+               inv_maxrounds = 15; // 15?
+       }
 }
 
 void invasion_Initialize()
index e934f8745a4e29bfbcee542ee2c9b1793ea815df..98322bcac0fcbcbb0ebfc570ff9c61b6449c3f9b 100644 (file)
@@ -4,6 +4,7 @@
 
 #define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
 int autocvar_g_invasion_teams;
+int autocvar_g_invasion_type;
 bool autocvar_g_invasion_team_spawns;
 bool g_invasion;
 void invasion_Initialize();
@@ -58,3 +59,7 @@ float inv_monsters_perteam[17];
 float inv_monsterskill;
 
 const float ST_INV_KILLS = 1;
+
+const int INV_TYPE_ROUND = 0; // round-based waves of enemies
+const int INV_TYPE_HUNT = 1; // clear the map of placed enemies
+const int INV_TYPE_STAGE = 2; // reach the end of the level
index b94b2533f3441a1e5955a13f54214b142e94a398..f69faa03a324de3e93d3136610a2835d9bbc781e 100644 (file)
@@ -84,14 +84,14 @@ void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this)
        this.takedamage = DAMAGE_NO;
        this.event_damage = func_null;
 
+       MUTATOR_CALLHOOK(PrepareExplosionByDamage, this, attacker);
+
        if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner)
        {
                this.owner = attacker;
                this.realowner = attacker;
        }
 
-       MUTATOR_CALLHOOK(PrepareExplosionByDamage, this, attacker);
-
        // do not explode NOW but in the NEXT FRAME!
        // because recursive calls to RadiusDamage are not allowed
        this.nextthink = time;
index f657789f8cc5f8a920f2fb096b89b7476405dcf7..8eaa0280a7588fcee50c53a49e73fb23de79c2cc 100644 (file)
@@ -30,7 +30,7 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vect
        float oldsolid = ent.dphitcontentsmask;
        if(!IS_CLIENT(ent))
                antilag = false; // no antilag for non-clients!
-       if (IS_PLAYER(ent) && ent.(weaponentity).m_weapon == WEP_RIFLE)
+       if (IS_PLAYER(ent) && (ent.(weaponentity).m_weapon.spawnflags & WEP_FLAG_PENETRATEWALLS))
                ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
        else
                ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
index 130bee7ab023805d72fd02799e399012692b277b..a538eafbb59c29b0ee036b4a1813626abb52c2f1 100644 (file)
@@ -2,7 +2,6 @@ models/ok_nade_counter/ok_nade_counter_01
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_01"
                blendfunc add
@@ -12,7 +11,6 @@ models/ok_nade_counter/ok_nade_counter_02
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_02"
                blendfunc add
@@ -22,7 +20,6 @@ models/ok_nade_counter/ok_nade_counter_03
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_03"
                blendfunc add
@@ -32,7 +29,6 @@ models/ok_nade_counter/ok_nade_counter_04
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_04"
                blendfunc add
@@ -42,7 +38,6 @@ models/ok_nade_counter/ok_nade_counter_05
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_05"
                blendfunc add
@@ -52,7 +47,6 @@ models/ok_nade_counter/ok_nade_counter_06
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_06"
                blendfunc add
@@ -62,7 +56,6 @@ models/ok_nade_counter/ok_nade_counter_07
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_07"
                blendfunc add
@@ -72,7 +65,6 @@ models/ok_nade_counter/ok_nade_counter_08
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_08"
                blendfunc add
@@ -82,9 +74,8 @@ models/ok_nade_counter/ok_nade_counter_09
 {
        dpnoshadow
        deformVertexes autosprite
-       dppolygonoffset -6000
        {
                map "models/ok_nade_counter/ok_nade_counter_09"
                blendfunc add
        }
-}
\ No newline at end of file
+}