]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'Mario/nade_updates'
authorMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 16:48:46 +0000 (03:48 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 16:48:46 +0000 (03:48 +1100)
55 files changed:
gamemodes.cfg
gfx/hud/default/nade_bg.tga [new file with mode: 0644]
gfx/hud/default/nade_nbg.tga [new file with mode: 0644]
gfx/hud/default/notify_nade.tga [new file with mode: 0644]
gfx/hud/default/notify_nade_heal.tga [new file with mode: 0644]
gfx/hud/default/notify_nade_ice.tga [new file with mode: 0644]
gfx/hud/default/notify_nade_napalm.tga [new file with mode: 0644]
mutators.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/progs.src
qcsrc/client/projectile.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/deathtypes.qh
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/nades.qc [new file with mode: 0644]
qcsrc/common/nades.qh [new file with mode: 0644]
qcsrc/common/notifications.qh
qcsrc/common/stats.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/aim.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/command/cmd.qc
qcsrc/server/csqcprojectile.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_ca.qc
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_keepaway.qc
qcsrc/server/mutators/gamemode_keyhunt.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator_buffs.qc
qcsrc/server/mutators/mutator_campcheck.qc
qcsrc/server/mutators/mutator_dodging.qc
qcsrc/server/mutators/mutator_minstagib.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/mutators/mutator_nades.qh
qcsrc/server/mutators/mutator_spawn_near_teammate.qc
qcsrc/server/mutators/mutator_touchexplode.qc
qcsrc/server/progs.src
qcsrc/server/spawnpoints.qh
qcsrc/server/sv_main.qc
qcsrc/server/teamplay.qc
qcsrc/server/w_electro.qc
qcsrc/server/w_minelayer.qc

index 2b8874cd2e10fa22fa273e6aaebdf2bce8f5ed21..dadd41013ae8dfee7fd3a0f3cc0e2c3f469fd062 100644 (file)
@@ -349,9 +349,12 @@ seta g_freezetag_point_leadlimit -1        "Freeze Tag point lead limit overriding the
 set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
 set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
 set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
+set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
 set g_freezetag_revive_falldamage 0 "Enable reviving from this amount of fall damage"
 set g_freezetag_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
 set g_freezetag_round_timelimit 180 "round time limit in seconds"
+set g_freezetag_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
 set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
 set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
 seta g_freezetag_teams_override 0
diff --git a/gfx/hud/default/nade_bg.tga b/gfx/hud/default/nade_bg.tga
new file mode 100644 (file)
index 0000000..ffe0e0f
Binary files /dev/null and b/gfx/hud/default/nade_bg.tga differ
diff --git a/gfx/hud/default/nade_nbg.tga b/gfx/hud/default/nade_nbg.tga
new file mode 100644 (file)
index 0000000..1868374
Binary files /dev/null and b/gfx/hud/default/nade_nbg.tga differ
diff --git a/gfx/hud/default/notify_nade.tga b/gfx/hud/default/notify_nade.tga
new file mode 100644 (file)
index 0000000..78dd0e2
Binary files /dev/null and b/gfx/hud/default/notify_nade.tga differ
diff --git a/gfx/hud/default/notify_nade_heal.tga b/gfx/hud/default/notify_nade_heal.tga
new file mode 100644 (file)
index 0000000..17e2408
Binary files /dev/null and b/gfx/hud/default/notify_nade_heal.tga differ
diff --git a/gfx/hud/default/notify_nade_ice.tga b/gfx/hud/default/notify_nade_ice.tga
new file mode 100644 (file)
index 0000000..f752808
Binary files /dev/null and b/gfx/hud/default/notify_nade_ice.tga differ
diff --git a/gfx/hud/default/notify_nade_napalm.tga b/gfx/hud/default/notify_nade_napalm.tga
new file mode 100644 (file)
index 0000000..bb84101
Binary files /dev/null and b/gfx/hud/default/notify_nade_napalm.tga differ
index 8ee7751e3f7e6e1fda870bb73e5e2296ba99cab5..4b79048677499c02e4af4a2ff9071b05702d72c7 100644 (file)
@@ -138,10 +138,11 @@ set g_random_gravity_negative 1000 "negative gravity multiplier"
 
 
 // =======
-//  nades
+//  Nades
 // =======
 set g_nades 0 "enable off-hand grenades"
 set g_nades_spawn 1 "give nades right away when player spawns rather than delaying entire refire"
+set g_nades_client_select 0 "allow client side selection of nade type"
 set g_nades_nade_lifetime 3.5
 set g_nades_nade_minforce 400
 set g_nades_nade_maxforce 2000
@@ -152,6 +153,73 @@ set g_nades_nade_edgedamage 90
 set g_nades_nade_radius 300
 set g_nades_nade_force 650
 set g_nades_nade_newton_style 0
+set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+
+seta cl_nade_timer 1 "show a visual timer for nades, 1 = only circle, 2 = circle with text"
+seta cl_nade_type 3
+seta cl_pokenade_type "zombie"
+
+// ------------
+//  Nade bonus
+// ------------
+//
+// How the nade bonus system works:
+// Each player has a score counter that is increased by some actions (eg: capping, fragging...)
+// Once this counter reaches its maximum, the player will receive a bonus grenade and the score counter resets
+// If the player dies all the bonus nades will be lost and the score counter resets
+// If g_nades_bonus_score_time is not zero, this score will increase or decrease over time
+//
+set g_nades_bonus 0 "Enable bonus grenades"
+set g_nades_bonus_client_select 0 "Allow client side selection of bonus nade type"
+set g_nades_bonus_type 2 "Type of the bonus grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+set g_nades_bonus_onstrength 1 "Always give bonus grenades to players that have the strength powerup"
+set g_nades_bonus_max 3 "Maximum number of bonus grenades"
+// Bonus score
+set g_nades_bonus_score_max   120 "Score value that will give a bonus nade"
+set g_nades_bonus_score_minor   5 "Score given for minor actions (pickups, regular frags etc.)"
+set g_nades_bonus_score_low    20 "Score given for frags and unfreezes"
+set g_nades_bonus_score_medium 30 "Score given for flag returns and flag carrier kills"
+set g_nades_bonus_score_high   60 "Score given for flag captures"
+set g_nades_bonus_score_spree  40 "Score given every spree of this many frags"
+set g_nades_bonus_score_time   -1 "Bonus nade score given per second (negative to have the score decay)"
+set g_nades_bonus_score_time_flagcarrier 2 "Bonus nade score given per second as flag carrier (negative to have the score decay)"
+
+// Napalm (2)
+set g_nades_napalm_blast 1 "Whether the napalm grenades also give damage with the usual grenade explosion"
+set g_nades_napalm_burntime 0.5 "Time that the fire from napalm will stick to the player"
+set g_nades_napalm_selfdamage 1 "Whether the player that tossed the nade can be harmed by its fire"
+// Napalm fireballs
+set g_nades_napalm_ball_count 6 "Number of fireballs emitted during the explosion"
+set g_nades_napalm_ball_spread 500 "Maximum force which the fireballs will have on explosion"
+set g_nades_napalm_ball_damageforcescale 4
+set g_nades_napalm_ball_damage 40
+set g_nades_napalm_ball_lifetime 7
+set g_nades_napalm_ball_radius 100 "Distance from the fireball within which you may get burned"
+// Napalm Fire fountain
+set g_nades_napalm_fountain_lifetime 3 "Time period during which extra fire mines are ejected"
+set g_nades_napalm_fountain_delay 0.5 "Delay between emissions by the fountain"
+set g_nades_napalm_fountain_damage 50 "Damage caused by the center of the fountain"
+set g_nades_napalm_fountain_edgedamage 20 "Damage caused by the edge of the fountain"
+set g_nades_napalm_fountain_radius 130
+
+// Ice (3)
+set g_nades_ice_freeze_time 3 "How long the ice field will last"
+set g_nades_ice_health      0 "How much health the player will have after being unfrozen"
+set g_nades_ice_explode     0 "Whether the ice nade should explode again once the ice field dissipated"
+set g_nades_ice_teamcheck   0 "Don't freeze teammates"
+
+// Spawn (5)
+set g_nades_spawn_count 3 "Number of times player will spawn at their spawn nade explosion location"
+
+// Heal (6)
+set g_nades_heal_time 5 "How long the heling field will last"
+set g_nades_heal_rate 30 "Health given per second"
+set g_nades_heal_friend 1 "Multiplier of health given to team mates"
+set g_nades_heal_foe   -2 "Multiplier of health given to enemies"
+
+// Pokenade (7)
+set g_nades_pokenade_monster_lifetime 150 "How long pokenade monster will survive"
+set g_nades_pokenade_monster_type "zombie" "Monster to spawn"
 
 
 // ============
index c948c9a70e1e7f9c34d7f1ba8d6009fd221691a9..c0e37ae62177b536a0d60a085ae6b056684241b8 100644 (file)
@@ -87,6 +87,9 @@ void CSQC_Init(void)
        registercvar("hud_usecsqc", "1");
        registercvar("scoreboard_columns", "default");
 
+       registercvar("cl_nade_type", "3");
+       registercvar("cl_pokenade_type", "zombie");
+
        gametype = 0;
 
        // hud_fields uses strunzone on the titles!
@@ -831,6 +834,7 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_SPAWNPOINT: Ent_ReadSpawnPoint(bIsNewEntity); break;
                case ENT_CLIENT_SPAWNEVENT: Ent_ReadSpawnEvent(bIsNewEntity); break;
                case ENT_CLIENT_NOTIFICATION: Read_Notification(bIsNewEntity); break;
+               case ENT_CLIENT_HEALING_ORB: ent_healer(); break;
 
                default:
                        //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
index 2a41ba13c673c601f9134052a5f4cba68425a5ee..a069faa46578dd07f5aa3600220bd233400185b6 100644 (file)
@@ -1155,15 +1155,20 @@ void CSQC_UpdateView(float w, float h)
 
        //else
        {
-               if(gametype == MAPINFO_TYPE_FREEZETAG)
+               if(getstati(STAT_FROZEN))
+                       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, ((getstatf(STAT_REVIVE_PROGRESS)) ? ('0.25 0.90 1' + ('1 0 0' * getstatf(STAT_REVIVE_PROGRESS)) + ('0 1 1' * getstatf(STAT_REVIVE_PROGRESS) * -1)) : '0.25 0.90 1'), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+               else if (getstatf(STAT_HEALING_ORB)>time)
+                       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, Nade_Color(NADE_TYPE_HEAL), autocvar_hud_colorflash_alpha*getstatf(STAT_HEALING_ORB_ALPHA), DRAWFLAG_ADDITIVE);
+               if(!intermission)
+               if(getstatf(STAT_NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death
                {
-                       if(getstati(STAT_FROZEN))
-                               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-                       if(getstatf(STAT_REVIVE_PROGRESS))
-                       {
-                               DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
-                               drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
-                       }
+                       DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * getstatf(STAT_NADE_TIMER)) - ('0 1 1' * getstatf(STAT_NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+                       drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+               }
+               else if(getstatf(STAT_REVIVE_PROGRESS))
+               {
+                       DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+                       drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
                }
 
                if(autocvar_r_letterbox == 0)
index 8516d32f6debeaf4faaa7fa539f5483a6e17eb86..28ef157e50d74bfde213a46b1c62d47b24adc0db 100644 (file)
@@ -443,3 +443,4 @@ string autocvar__cl_playermodel;
 float autocvar_cl_deathglow;
 float autocvar_developer_csqcentities;
 float autocvar_g_jetpack_attenuation;
+float autocvar_cl_nade_timer;
index e1c95025892e272c556f33edbc86275df630a21a..ab61eb3c3eb4c071c549467e1b9b556deefadc76 100644 (file)
@@ -886,6 +886,54 @@ string GetAmmoPicture(float i)
        }
 }
 
+void DrawNadeScoreBar(vector myPos, vector mySize, vector color)
+{
+       
+       HUD_Panel_DrawProgressBar(
+               myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x, 
+               mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x, 
+               autocvar_hud_panel_ammo_progressbar_name, 
+               getstatf(STAT_NADE_BONUS_SCORE), 0, 0, color, 
+               autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+
+}
+
+void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expand_time)
+{
+       float theAlpha = 1, a, b;
+       vector nade_color, picpos, numpos;
+       
+       nade_color = Nade_Color(getstati(STAT_NADE_BONUS_TYPE));
+       
+       a = getstatf(STAT_NADE_BONUS);
+       b = getstatf(STAT_NADE_BONUS_SCORE);
+       
+       if(autocvar_hud_panel_ammo_iconalign)
+       {
+               numpos = myPos;
+               picpos = myPos + eX * 2 * mySize_y;
+       }
+       else
+       {
+               numpos = myPos + eX * mySize_y;
+               picpos = myPos;
+       }
+
+       DrawNadeScoreBar(myPos, mySize, nade_color);
+
+       if(b > 0 || a > 0)
+       {
+               if(autocvar_hud_panel_ammo_text)
+                       drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+               
+               if(draw_expanding)
+                       drawpic_aspect_skin_expanding(picpos, "nade_nbg", '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, expand_time);
+                       
+               drawpic_aspect_skin(picpos, "nade_bg" , '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+               drawpic_aspect_skin(picpos, "nade_nbg" , '1 1 0' * mySize_y, nade_color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+       }
+}
+
 void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_selected, float infinite_ammo)
 {
        float a;
@@ -942,6 +990,9 @@ void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_s
                drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL);
 }
 
+float nade_prevstatus;
+float nade_prevframe;
+float nade_statuschange_time;
 void HUD_Ammo(void)
 {
        if(hud != HUD_NORMAL) return;
@@ -966,21 +1017,39 @@ void HUD_Ammo(void)
                mySize -= '2 2 0' * panel_bg_padding;
        }
 
-       const float AMMO_COUNT = 4;
        float rows = 0, columns, row, column;
+       float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
+       float draw_nades = (nade_cnt > 0 || nade_score > 0), nade_statuschange_elapsedtime;
+       float total_ammo_count;
+
        vector ammo_size;
+       float AMMO_COUNT = 4;
        if (autocvar_hud_panel_ammo_onlycurrent)
-               ammo_size = mySize;
+               total_ammo_count = 1;
        else
+               total_ammo_count = AMMO_COUNT - 1; // fuel
+
+       if(draw_nades)
        {
-               rows = mySize_y/mySize_x;
-               rows = bound(1, floor((sqrt(4 * (3/1) * rows * AMMO_COUNT + rows * rows) + rows + 0.5) / 2), AMMO_COUNT);
-               //                               ^^^ ammo item aspect goes here
+               ++total_ammo_count;
+               if (nade_cnt != nade_prevframe)
+               {
+                       nade_statuschange_time = time;
+                       nade_prevstatus = nade_prevframe;
+                       nade_prevframe = nade_cnt;
+               }
+       }
+       else
+               nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
 
-               columns = ceil(AMMO_COUNT/rows);
+       rows = mySize_y/mySize_x;
+       rows = bound(1, floor((sqrt(4 * (3/1) * rows * (total_ammo_count) + rows * rows) + rows + 0.5) / 2), (total_ammo_count));
+       //                               ^^^ ammo item aspect goes here
 
-               ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
-       }
+       columns = ceil((total_ammo_count)/rows);
+
+       ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+       
 
        local vector offset = '0 0 0'; // fteqcc sucks
        float newSize;
@@ -1001,6 +1070,9 @@ void HUD_Ammo(void)
 
        float i, stat_items, currently_selected, infinite_ammo;
        infinite_ammo = FALSE;
+
+       row = column = 0;
+
        if (autocvar_hud_panel_ammo_onlycurrent)
        {
                if(autocvar__hud_configure)
@@ -1021,13 +1093,19 @@ void HUD_Ammo(void)
                                }
                        }
                }
+
+               ++row;
+               if(row >= rows)
+               {
+                       row = 0;
+                       column = column + 1;
+               }
        }
        else
        {
                stat_items = getstati(STAT_ITEMS, 0, 24);
                if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
                        infinite_ammo = TRUE;
-               row = column = 0;
                for (i = 0; i < AMMO_COUNT; ++i) {
                        currently_selected = stat_items & GetAmmoItemCode(i);
                        DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo);
@@ -1040,6 +1118,15 @@ void HUD_Ammo(void)
                }
        }
 
+       if (draw_nades)
+       {
+               nade_statuschange_elapsedtime = time - nade_statuschange_time;
+
+               float f = bound(0, nade_statuschange_elapsedtime*2, 1);
+
+               DrawAmmoNades(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
+       }
+
        draw_endBoldFont();
 }
 
index b7281c562124b2c8dc0c20be64254d23095fa8da..86a8b43395e199d83a1dc1676f5a7d234cf1a27f 100644 (file)
@@ -17,6 +17,7 @@ Defs.qc
 
 ../common/teams.qh
 ../common/util.qh
+../common/nades.qh
 ../common/buffs.qh
 ../common/test.qh
 ../common/counting.qh
@@ -119,6 +120,7 @@ command/cl_cmd.qc
 
 ../common/monsters/monsters.qc
 
+../common/nades.qc
 ../common/buffs.qc
 
 ../warpzonelib/anglestransform.qc
index 8cdcdf470c18c47dd2a97e5508f334b5ab647116..8ff5e0f911627e78be6315aa196989debded3b1c 100644 (file)
@@ -101,24 +101,16 @@ void Projectile_Draw()
                        case PROJECTILE_GRENADE_BOUNCING:
                                rot = '0 -1000 0'; // sideways
                                break;
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                       case PROJECTILE_NADE_YELLOW_BURN:
-                       case PROJECTILE_NADE_YELLOW:
-                       case PROJECTILE_NADE_PINK_BURN:
-                       case PROJECTILE_NADE_PINK:
-                       case PROJECTILE_NADE_BURN:
-                       case PROJECTILE_NADE:
-                               rot = self.avelocity;
-                               break;
                        case PROJECTILE_HOOKBOMB:
                                rot = '1000 0 0'; // forward
                                break;
                        default:
                                break;
                }
+
+               if(Nade_IDFromProjectile(self.cnt) != 0)
+                       rot = self.avelocity;
+
                self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime)));
        }
 
@@ -136,18 +128,6 @@ void Projectile_Draw()
        trailorigin = self.origin;
        switch(self.cnt)
        {
-           case PROJECTILE_NADE_RED_BURN:
-               case PROJECTILE_NADE_RED:
-               case PROJECTILE_NADE_BLUE_BURN:
-               case PROJECTILE_NADE_BLUE:
-               case PROJECTILE_NADE_YELLOW_BURN:
-               case PROJECTILE_NADE_YELLOW:
-               case PROJECTILE_NADE_PINK_BURN:
-               case PROJECTILE_NADE_PINK:
-               case PROJECTILE_NADE_BURN:
-               case PROJECTILE_NADE:
-                       trailorigin += v_up * 4;
-                       break;
                case PROJECTILE_GRENADE:
                case PROJECTILE_GRENADE_BOUNCING:
                        trailorigin += v_right * 1 + v_forward * -10;
@@ -155,6 +135,10 @@ void Projectile_Draw()
                default:
                        break;
        }
+
+       if(Nade_IDFromProjectile(self.cnt) != 0)
+               trailorigin += v_up * 4;
+
        if(drawn)
                Projectile_DrawTrail(trailorigin);
        else
@@ -274,6 +258,8 @@ void Ent_Projectile()
                        self.fade_time = 0;
                        self.fade_rate = 0;
                }
+
+               self.team = ReadByte() - 1;
        }
 
        if(f & 2)
@@ -302,6 +288,7 @@ void Ent_Projectile()
                        case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
                        case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
                        case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
+                       case PROJECTILE_NAPALM_FOUNTAIN: //self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("torch_small"); break;
                        case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough
                        case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough
                        case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
@@ -322,18 +309,8 @@ void Ent_Projectile()
                        case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
                        case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 
-                       case PROJECTILE_NADE_RED: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red"); break;
-                       case PROJECTILE_NADE_RED_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red_burn"); break;
-                       case PROJECTILE_NADE_BLUE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue"); break;
-                       case PROJECTILE_NADE_BLUE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue_burn"); break;
-                       case PROJECTILE_NADE_YELLOW: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow"); break;
-                       case PROJECTILE_NADE_YELLOW_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow_burn"); break;
-                       case PROJECTILE_NADE_PINK: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink"); break;
-                       case PROJECTILE_NADE_PINK_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink_burn"); break;
-                       case PROJECTILE_NADE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade"); break;
-                       case PROJECTILE_NADE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_burn"); break;
-
                        default:
+                               if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; }
                                error("Received invalid CSQC projectile, can't work with this!");
                                break;
                }
@@ -366,17 +343,6 @@ void Ent_Projectile()
                                self.mins = '-3 -3 -3';
                                self.maxs = '3 3 3';
                                break;
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.scale = 1.5;
-                               self.avelocity = randomvec() * 720;
-                               break;
                        case PROJECTILE_GRENADE_BOUNCING:
                                self.mins = '-3 -3 -3';
                                self.maxs = '3 3 3';
@@ -385,23 +351,6 @@ void Ent_Projectile()
                                self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor;
                                self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop;
                                break;
-                       case PROJECTILE_NADE_RED_BURN:
-                       case PROJECTILE_NADE_RED:
-                       case PROJECTILE_NADE_BLUE_BURN:
-                       case PROJECTILE_NADE_BLUE:
-                       case PROJECTILE_NADE_YELLOW_BURN:
-                       case PROJECTILE_NADE_YELLOW:
-                       case PROJECTILE_NADE_PINK_BURN:
-                       case PROJECTILE_NADE_PINK:
-                       case PROJECTILE_NADE_BURN:
-                       case PROJECTILE_NADE:
-                               self.mins = '-16 -16 -16';
-                               self.maxs = '16 16 16';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.scale = 1.5;
-                               self.avelocity = randomvec() * 720;
-                               break;
                        case PROJECTILE_SHAMBLER_LIGHTNING:
                                self.mins = '-8 -8 -8';
                                self.maxs = '8 8 8';
@@ -432,6 +381,7 @@ void Ent_Projectile()
                                self.move_movetype = MOVETYPE_BOUNCE;
                                self.move_touch = func_null;
                                break;
+                       case PROJECTILE_NAPALM_FOUNTAIN:
                        case PROJECTILE_FIREBALL:
                                loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM);
                                self.mins = '-16 -16 -16';
@@ -488,6 +438,22 @@ void Ent_Projectile()
                        default:
                                break;
                }
+
+               if(Nade_IDFromProjectile(self.cnt) != 0)
+               {
+                       self.mins = '-16 -16 -16';
+                       self.maxs = '16 16 16';
+                       self.colormod = Nade_Color(Nade_IDFromProjectile(self.cnt));
+                       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+                       self.move_movetype = MOVETYPE_BOUNCE;
+                       self.move_touch = func_null;
+                       self.scale = 1.5;
+                       self.avelocity = randomvec() * 720;
+                       
+                       if(Nade_IDFromProjectile(self.cnt) == NADE_TYPE_TRANSLOCATE)
+                               self.solid = SOLID_TRIGGER;
+               }
+
                setsize(self, self.mins, self.maxs);
        }
 
@@ -526,6 +492,7 @@ void Projectile_Precache()
        precache_model("models/rocket.md3");
        precache_model("models/tagrocket.md3");
        precache_model("models/tracer.mdl");
+       precache_model("models/sphere/sphere.md3");
 
        precache_model("models/weapons/v_ok_grenade.md3");
 
index 38d1579282eed5650d36d556b21a4d4041cb6206..7ee34672c5caa087b242ef27aad7b8d3e64f8363 100644 (file)
@@ -310,7 +310,7 @@ string spritelookuptext(string s)
                case "item-shield": return _("Shield");
                case "item-fuelregen": return _("Fuel regen");
                case "item-jetpack": return _("Jet Pack");
-               case "freezetag_frozen": return _("Frozen!");
+               case "frozen": return _("Frozen!");
                case "tagged-target": return _("Tagged");
                case "vehicle": return _("Vehicle");
                default: return s;
index 7c8e9b35d32ec342abc38aa5860edac3edef9433..fb6d781c0c1e16e10e5cfb4fa3a591537b311050 100644 (file)
@@ -101,6 +101,8 @@ const float ENT_CLIENT_TURRET = 40;
 const float ENT_CLIENT_AUXILIARYXHAIR = 50;
 const float ENT_CLIENT_VEHICLE = 60;
 
+const float ENT_CLIENT_HEALING_ORB = 80;
+
 const float SPRITERULE_DEFAULT = 0;
 const float SPRITERULE_TEAMPLAY = 1;
 
@@ -271,17 +273,6 @@ const float PROJECTILE_BUMBLE_BEAM = 31;
 const float PROJECTILE_MAGE_SPIKE = 32;
 const float PROJECTILE_SHAMBLER_LIGHTNING = 33;
 
-const float PROJECTILE_NADE_RED = 50;
-const float PROJECTILE_NADE_RED_BURN = 51;
-const float PROJECTILE_NADE_BLUE = 52;
-const float PROJECTILE_NADE_BLUE_BURN = 53;
-const float PROJECTILE_NADE_YELLOW = 54;
-const float PROJECTILE_NADE_YELLOW_BURN = 55;
-const float PROJECTILE_NADE_PINK = 56;
-const float PROJECTILE_NADE_PINK_BURN = 57;
-const float PROJECTILE_NADE = 58;
-const float PROJECTILE_NADE_BURN = 59;
-
 const float SPECIES_HUMAN = 0;
 const float SPECIES_ROBOT_SOLID = 1;
 const float SPECIES_ALIEN = 2;
index 595cad7eea387401d507ee998b6031b663f91e1d..c8512cdcf25c7039bc352946e328bdb0566c19a8 100644 (file)
        DEATHTYPE(DEATH_MONSTER_WYVERN,                 DEATH_SELF_MON_WYVERN,                          DEATH_MURDER_MONSTER,              NORMAL_POS) \
        DEATHTYPE(DEATH_MONSTER_ZOMBIE_JUMP,    DEATH_SELF_MON_ZOMBIE_JUMP,                     DEATH_MURDER_MONSTER,              NORMAL_POS) \
        DEATHTYPE(DEATH_MONSTER_ZOMBIE_MELEE,   DEATH_SELF_MON_ZOMBIE_MELEE,            DEATH_MURDER_MONSTER,              DEATH_MONSTER_LAST) \
-       DEATHTYPE(DEATH_NADE,                                   DEATH_SELF_NADE,                                        DEATH_MURDER_NADE,                         NORMAL_POS) \
+       DEATHTYPE(DEATH_NADE,                   DEATH_SELF_NADE,                    DEATH_MURDER_NADE,             NORMAL_POS) \
+       DEATHTYPE(DEATH_NADE_NAPALM,            DEATH_SELF_NADE_NAPALM,             DEATH_MURDER_NADE_NAPALM,      NORMAL_POS) \
+       DEATHTYPE(DEATH_NADE_ICE,               DEATH_SELF_NADE_ICE,                DEATH_MURDER_NADE_ICE,         NORMAL_POS) \
+       DEATHTYPE(DEATH_NADE_ICE_FREEZE,        DEATH_SELF_NADE_ICE_FREEZE,         DEATH_MURDER_NADE_ICE_FREEZE,  NORMAL_POS) \
+       DEATHTYPE(DEATH_NADE_HEAL,              DEATH_SELF_NADE_HEAL,               DEATH_MURDER_NADE_HEAL,        NORMAL_POS) \
        DEATHTYPE(DEATH_NOAMMO,                 DEATH_SELF_NOAMMO,                  NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_ROT,                    DEATH_SELF_ROT,                     NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_SHOOTING_STAR,          DEATH_SELF_SHOOTING_STAR,           DEATH_MURDER_SHOOTING_STAR,    NORMAL_POS) \
index bda48d5cb91dee7ae2fd20b5c1cb3d001c17d1b7..26bb0a9afda23c20909e5e35c5c7ed03a5df10a6 100644 (file)
@@ -60,7 +60,7 @@ float friend_needshelp(entity e)
                return FALSE;
        if(DIFF_TEAM(e, self) && e != self.monster_owner)
                return FALSE;
-       if(e.freezetag_frozen)
+       if(e.frozen)
                return FALSE;
        if(!IS_PLAYER(e))
                return ((e.flags & FL_MONSTER) && e.health < e.max_health);
index 34e7ceb9901ebbb30583e88b3862ba3204cd28fb..6d06de50ebb30e9c8ff98ee6c5fefcb5542dbcf7 100644 (file)
@@ -94,7 +94,7 @@ float monster_isvalidtarget (entity targ, entity ent)
        if(SAME_TEAM(targ, ent))
                return FALSE; // enemy is on our team
 
-       if (targ.freezetag_frozen)
+       if (targ.frozen)
                return FALSE; // ignore frozen
 
        if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
@@ -480,7 +480,7 @@ vector monster_pickmovetarget(entity targ)
                
                if((self.enemy == world)
                        || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
-                       || (self.enemy.freezetag_frozen)
+                       || (self.enemy.frozen)
                        || (self.enemy.flags & FL_NOTARGET)
                        || (self.enemy.alpha < 0.5)
                        || (self.enemy.takedamage == DAMAGE_NO)
@@ -605,6 +605,51 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
 
        entity targ;
 
+       if(self.frozen == 2)
+       {
+               self.revive_progress = bound(0, self.revive_progress + self.ticrate * self.revive_speed, 1);
+               self.health = max(1, self.revive_progress * self.max_health);
+               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+               movelib_beak_simple(stopspeed);
+               self.frame = manim_idle;
+
+               self.enemy = world;
+               self.nextthink = time + self.ticrate;
+
+               if(self.revive_progress >= 1)
+                       Unfreeze(self);
+
+               return;
+       }
+       else if(self.frozen == 3)
+       {
+               self.revive_progress = bound(0, self.revive_progress - self.ticrate * self.revive_speed, 1);
+               self.health = max(0, autocvar_g_nades_ice_health + (self.max_health-autocvar_g_nades_ice_health) * self.revive_progress );
+
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+               movelib_beak_simple(stopspeed);
+               self.frame = manim_idle;
+
+               self.enemy = world;
+               self.nextthink = time + self.ticrate;
+
+               if(self.health < 1)
+               {
+                       Unfreeze(self);
+                       self.health = 0;
+                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+               }
+
+               else if ( self.revive_progress <= 0 )
+                       Unfreeze(self);
+
+               return;
+       }
+
        if(self.flags & FL_SWIM)
        {
                if(self.waterlevel < WATERLEVEL_WETFEET)
@@ -776,6 +821,9 @@ void monster_remove(entity mon)
        if(mon.weaponentity)
                remove(mon.weaponentity);
 
+       if(mon.iceblock)
+               remove(mon.iceblock);
+
        WaypointSprite_Kill(mon.sprite);
 
        remove(mon);
@@ -827,6 +875,8 @@ void monsters_reset()
        setorigin(self, self.pos1);
        self.angles = self.pos2;
 
+       Unfreeze(self); // remove any icy remains
+
        self.health = self.max_health;
        self.velocity = '0 0 0';
        self.enemy = world;
@@ -860,6 +910,12 @@ void monster_die(entity attacker, float gibbed)
        self.nextthink = time;
        self.monster_lifetime = time + 5;
 
+       if(self.frozen)
+       {
+               Unfreeze(self); // remove any icy remains
+               self.health = 0; // reset by Unfreeze
+       }
+
        monster_dropitem();
 
        MonsterSound(monstersound_death, 0, FALSE, CH_VOICE);
@@ -900,6 +956,9 @@ void monster_die(entity attacker, float gibbed)
 
 void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
+       if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
+               return;
+
        if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
                return;
 
diff --git a/qcsrc/common/nades.qc b/qcsrc/common/nades.qc
new file mode 100644 (file)
index 0000000..03b1552
--- /dev/null
@@ -0,0 +1,82 @@
+.float healer_lifetime;
+.float healer_radius;
+
+#ifdef SVQC
+float healer_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_HEALING_ORB);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               WriteByte(MSG_ENTITY, self.healer_lifetime);
+               //WriteByte(MSG_ENTITY, self.ltime - time + 1);
+               WriteShort(MSG_ENTITY, self.healer_radius);
+               // round time delta to a 1/10th of a second
+               WriteByte(MSG_ENTITY, (self.ltime - time)*10.0+0.5);
+       }
+
+       return TRUE;
+}
+#endif // SVQC
+
+#ifdef CSQC
+.float ltime;
+void healer_draw()
+{
+       float dt = time - self.move_time;
+       self.move_time = time;
+       if(dt <= 0)
+               return;
+
+       self.alpha = (self.ltime - time) / self.healer_lifetime;
+       self.scale = min((1 - self.alpha)*self.healer_lifetime*4,1)*self.healer_radius;
+
+}
+
+void healer_setup()
+{
+       setmodel(self, "models/ctf/shield.md3");
+
+       setorigin(self, self.origin);
+       
+       float model_radius = self.maxs_x;
+       vector size = '1 1 1' * self.healer_radius / 2;
+       setsize(self,-size,size);
+       self.healer_radius = self.healer_radius/model_radius*0.6;
+
+       self.draw = healer_draw;
+       self.health = 255;
+       self.movetype = MOVETYPE_NONE;
+       self.solid = SOLID_NOT;
+       self.drawmask = MASK_NORMAL;
+       self.scale = 0.01;
+       self.avelocity = self.move_avelocity = '7 0 11';
+       self.colormod = '1 0 0';
+       self.renderflags |= RF_ADDITIVE;
+}
+
+void ent_healer()
+{
+       float sf = ReadByte();
+
+       if(sf & TNSF_SETUP)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+
+               self.healer_lifetime = ReadByte();
+               self.healer_radius = ReadShort();
+               self.ltime = time + ReadByte()/10.0;
+               //self.ltime = time + self.healer_lifetime;
+
+               healer_setup();
+       }
+}
+#endif // CSQC
\ No newline at end of file
diff --git a/qcsrc/common/nades.qh b/qcsrc/common/nades.qh
new file mode 100644 (file)
index 0000000..1004e1e
--- /dev/null
@@ -0,0 +1,103 @@
+// use slots 70-100
+const float PROJECTILE_NADE = 71;
+const float PROJECTILE_NADE_BURN = 72;
+const float PROJECTILE_NADE_NAPALM = 73;
+const float PROJECTILE_NADE_NAPALM_BURN = 74;
+const float PROJECTILE_NAPALM_FOUNTAIN = 75;
+const float PROJECTILE_NADE_ICE = 76;
+const float PROJECTILE_NADE_ICE_BURN = 77;
+const float PROJECTILE_NADE_TRANSLOCATE = 78;
+const float PROJECTILE_NADE_SPAWN = 79;
+const float PROJECTILE_NADE_HEAL = 80;
+const float PROJECTILE_NADE_HEAL_BURN = 81;
+const float PROJECTILE_NADE_MONSTER = 82;
+const float PROJECTILE_NADE_MONSTER_BURN = 83;
+
+const float NADE_TYPE_NORMAL = 1;
+const float NADE_TYPE_NAPALM = 2;
+const float NADE_TYPE_ICE = 3;
+const float NADE_TYPE_TRANSLOCATE = 4;
+const float NADE_TYPE_SPAWN = 5;
+const float NADE_TYPE_HEAL = 6;
+const float NADE_TYPE_MONSTER = 7;
+
+const float NADE_TYPE_LAST = 7; // a check to prevent using higher values & crashing
+
+vector Nade_Color(float nadeid)
+{
+       switch(nadeid)
+       {
+               case NADE_TYPE_NORMAL: return '1 1 1';
+               case NADE_TYPE_NAPALM: return '2 0.5 0';
+               case NADE_TYPE_ICE: return '0 0.5 2';
+               case NADE_TYPE_TRANSLOCATE: return '1 0.0625 1';
+               case NADE_TYPE_SPAWN: return '1 0.9 0.06';
+        case NADE_TYPE_HEAL: return '1 0 0';
+               case NADE_TYPE_MONSTER: return '1 0.5 0';
+       }
+
+       return '0 0 0';
+}
+
+float Nade_IDFromProjectile(float proj)
+{
+       switch(proj)
+       {
+               case PROJECTILE_NADE:
+               case PROJECTILE_NADE_BURN: return NADE_TYPE_NORMAL;
+               case PROJECTILE_NADE_NAPALM:
+               case PROJECTILE_NADE_NAPALM_BURN: return NADE_TYPE_NAPALM;
+               case PROJECTILE_NADE_ICE:
+               case PROJECTILE_NADE_ICE_BURN: return NADE_TYPE_ICE;
+               case PROJECTILE_NADE_TRANSLOCATE: return NADE_TYPE_TRANSLOCATE;
+               case PROJECTILE_NADE_SPAWN: return NADE_TYPE_SPAWN;
+        case PROJECTILE_NADE_HEAL:
+        case PROJECTILE_NADE_HEAL_BURN: return NADE_TYPE_HEAL;
+               case PROJECTILE_NADE_MONSTER:
+               case PROJECTILE_NADE_MONSTER_BURN: return NADE_TYPE_MONSTER;
+       }
+
+       return 0;
+}
+
+float Nade_ProjectileFromID(float proj, float burn)
+{
+       switch(proj)
+       {
+               case NADE_TYPE_NORMAL: return (burn) ? PROJECTILE_NADE_BURN : PROJECTILE_NADE;
+               case NADE_TYPE_NAPALM: return (burn) ? PROJECTILE_NADE_NAPALM_BURN : PROJECTILE_NADE_NAPALM;
+               case NADE_TYPE_ICE: return (burn) ? PROJECTILE_NADE_ICE_BURN : PROJECTILE_NADE_ICE;
+               case NADE_TYPE_TRANSLOCATE: return PROJECTILE_NADE_TRANSLOCATE;
+               case NADE_TYPE_SPAWN: return PROJECTILE_NADE_SPAWN;
+        case NADE_TYPE_HEAL: return (burn) ? PROJECTILE_NADE_HEAL_BURN : PROJECTILE_NADE_HEAL;
+               case NADE_TYPE_MONSTER: return (burn) ? PROJECTILE_NADE_MONSTER_BURN : PROJECTILE_NADE_MONSTER;
+       }
+
+       return 0;
+}
+
+string Nade_TrailEffect(float proj, float nade_team)
+{
+       switch(proj)
+       {
+               case PROJECTILE_NADE: return strcat("nade_", Static_Team_ColorName_Lower(nade_team));
+               case PROJECTILE_NADE_BURN: return strcat("nade_", Static_Team_ColorName_Lower(nade_team), "_burn");
+               case PROJECTILE_NADE_NAPALM: return "TR_ROCKET";
+               case PROJECTILE_NADE_NAPALM_BURN: return "spiderbot_rocket_thrust";
+               case PROJECTILE_NADE_ICE: return "TR_NEXUIZPLASMA";
+               case PROJECTILE_NADE_ICE_BURN: return "wakizashi_rocket_thrust";
+               case PROJECTILE_NADE_TRANSLOCATE: return "TR_CRYLINKPLASMA";
+               case PROJECTILE_NADE_SPAWN: return "nade_yellow";
+        case PROJECTILE_NADE_HEAL: return "nade_red";
+        case PROJECTILE_NADE_HEAL_BURN: return "nade_red_burn";
+               case PROJECTILE_NADE_MONSTER: return "nade_red";
+               case PROJECTILE_NADE_MONSTER_BURN: return "nade_red_burn";
+       }
+       
+       return "";
+}
+
+#ifdef CSQC
+// misc functions
+void ent_healer();
+#endif // CSQC
index 9c55e457b947cd479ba7fc6ac2d98d2f93913b35..963a7d89dd0b1b282c98b8969fed2977a1d4ea21 100644 (file)
@@ -360,7 +360,11 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_lava",          _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER,           3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
-       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade",          _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_NAPALM,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_napalm",   _("^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s"), _("^BG%s%s^K1 got too close to a napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE,          3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE_FREEZE,   3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_ice",      _("^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_HEAL,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_nade_heal",     _("^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SWAMP,             3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_slime",         _("^BG%s%s^K1 was preserved by ^BG%s^K1%s%s"), "") \
@@ -390,7 +394,6 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC,             2, 1, "s1 s2loc spree_lost", "s1",       "notify_selfkill",      _("^BG%s^K1 died%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_lava",          _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
-       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE,                2, 1, "s1 s2loc spree_lost", "s1",           "notify_death",                 _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW,   2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH,  2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
@@ -399,6 +402,11 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN,          2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP,     2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 joins the Zombies%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE,    2, 1, "s1 s2loc spree_lost", "s1",               "notify_death",                 _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE,                2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade",          _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_NAPALM,         2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_napalm",   _("^BG%s^K1 was burned to death by their own Napalm Nade%s%s"), _("^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE_FREEZE,     2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_ice",      _("^BG%s^K1 was frozen to death by their own Ice Nade%s%s"), _("^BG%s^K1 felt a little chilly%s%s")) \
+       MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_HEAL,           2, 1, "s1 s2loc spree_lost", "s1",       "notify_nade_heal",     _("^BG%s^K1's Healing Nade didn't quite heal them%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO,              2, 1, "s1 s2loc spree_lost", "s1",       "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT,                 2, 1, "s1 s2loc spree_lost", "s1",       "notify_death",         _("^BG%s^K1 rotted away%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR,       2, 1, "s1 s2loc spree_lost", "s1",       "notify_shootingstar",  _("^BG%s^K1 became a shooting star%s%s"), "") \
@@ -433,6 +441,7 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE,               2, 0, "s1 s2", "",                       "",                     _("^BG%s^K1 was frozen by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED,              2, 0, "s1 s2", "",                       "",                     _("^BG%s^K3 was revived by ^BG%s"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by falling"), "") \
+       MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_NADE,         1, 0, "s1", "",                          "",                     _("^BG%s^K3 was revived by their Nade explosion"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED,         1, 1, "s1 f1", "",                       "",                     _("^BG%s^K3 was automatically revived after %s second(s)"), "") \
        MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4,             0, 0, "", "",                            "",                     _("^TC^TT^BG team wins the round"), "") \
        MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN,               1, 0, "s1", "",                          "",                     _("^BG%s^BG wins the round"), "") \
@@ -585,6 +594,7 @@ void Send_Notification_WOCOVA(
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE,  1, 4, "spree_cen s1 frag_stats",  NO_CPID, "0 0", _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE,     1, 2, "spree_cen s1 frag_ping",   NO_CPID, "0 0", _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s")) \
        MSG_CENTER_NOTIF(1, CENTER_NADE_THROW,                          0, 0, "",             CPID_NADES,          "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_NADE_BONUS,                          0, 0, "",             CPID_NADES,          "0 0", _("^F2You got a ^K1BONUS GRENADE^F2!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_AUTOTEAMCHANGE,   0, 1, "death_team",   NO_CPID,             "0 0", _("^BGYou have been moved into a different team\nYou are now on: %s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_BETRAYAL,         0, 0, "",             NO_CPID,             "0 0", _("^K1Don't shoot your team mates!"), _("^K1Don't go against your team mates!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_CAMP,             0, 0, "",             NO_CPID,             "0 0", _("^K1Die camper!"), _("^K1Reconsider your tactics, camper!")) \
@@ -597,6 +607,9 @@ void Send_Notification_WOCOVA(
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA,             0, 0, "",             NO_CPID,             "0 0", _("^K1You couldn't stand the heat!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER,          0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE,                             0, 0, "",                         NO_CPID,                         "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_NAPALM,              0, 0, "",                         NO_CPID,                         "0 0", _("^K1Hanging around a napalm explosion is bad!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_ICE_FREEZE,  0, 0, "",                         NO_CPID,                         "0 0", _("^K1You got a little bit too cold!"), _("^K1You felt a little chilly!")) \
+       MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_HEAL,        0, 0, "",             NO_CPID,             "0 0", _("^K1Your Healing Nade is a bit defective"), "") \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO,           0, 0, "",             NO_CPID,             "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT,              0, 0, "",             NO_CPID,             "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
        MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_SHOOTING_STAR,    0, 0, "",             NO_CPID,             "0 0", _("^K1You became a shooting star!"), "") \
@@ -624,7 +637,7 @@ void Send_Notification_WOCOVA(
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You froze ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN,            1, 0, "s1",           NO_CPID,             "0 0", _("^K1You were frozen by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE,            1, 0, "s1",           NO_CPID,             "0 0", _("^K3You revived ^BG%s"), "") \
-       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_FALL,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF,       0, 0, "",             NO_CPID,             "0 0", _("^K3You revived yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED,           1, 0, "s1",           NO_CPID,             "0 0", _("^K3You were revived by ^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED,      0, 1, "f1",           NO_CPID,             "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
        MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4,          0, 0, "",             CPID_ROUND,          "0 0", _("^TC^TT^BG team wins the round"), "") \
@@ -707,6 +720,10 @@ void Send_Notification_WOCOVA(
        MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA,                    NO_MSG,        INFO_DEATH_MURDER_LAVA,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER,                 NO_MSG,        INFO_DEATH_MURDER_MONSTER,                 CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE,                    NO_MSG,        INFO_DEATH_MURDER_NADE,                    NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_NAPALM,             NO_MSG,        INFO_DEATH_MURDER_NADE_NAPALM,             NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE,                NO_MSG,        INFO_DEATH_MURDER_NADE_ICE,                NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG,        INFO_DEATH_MURDER_NADE_ICE_FREEZE,         NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_HEAL,               NO_MSG,        INFO_DEATH_MURDER_NADE_HEAL,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR,           NO_MSG,        INFO_DEATH_MURDER_SHOOTING_STAR,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME,                   NO_MSG,        INFO_DEATH_MURDER_SLIME,                   NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP,                   NO_MSG,        INFO_DEATH_MURDER_SWAMP,                   NO_MSG) \
@@ -745,6 +762,10 @@ void Send_Notification_WOCOVA(
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP,                   NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_JUMP,                   CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE,                  NO_MSG,        INFO_DEATH_SELF_MON_ZOMBIE_MELEE,                  CENTER_DEATH_SELF_MONSTER) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NADE,                                              NO_MSG,                INFO_DEATH_SELF_NADE,                                      CENTER_DEATH_SELF_NADE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_NAPALM,                               NO_MSG,                INFO_DEATH_SELF_NADE_NAPALM,                       CENTER_DEATH_SELF_NADE_NAPALM) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE,                                  NO_MSG,                INFO_DEATH_SELF_NADE_ICE,                                  CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE_FREEZE,           NO_MSG,                INFO_DEATH_SELF_NADE_ICE_FREEZE,           CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+       MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_HEAL,                 NO_MSG,        INFO_DEATH_SELF_NADE_HEAL,                 CENTER_DEATH_SELF_NADE_HEAL) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO,                    NO_MSG,        INFO_DEATH_SELF_NOAMMO,                    CENTER_DEATH_SELF_NOAMMO) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_ROT,                       NO_MSG,        INFO_DEATH_SELF_ROT,                       CENTER_DEATH_SELF_ROT) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR,             NO_MSG,        INFO_DEATH_SELF_SHOOTING_STAR,             CENTER_DEATH_SELF_SHOOTING_STAR) \
index 7965597d1bc189ae75e6acacaa48d0d84245dab9..793582e1264b092483e2b7e9839dd27e847b61a1 100644 (file)
@@ -71,7 +71,7 @@ const float STAT_VEHICLESTAT_AMMO2      = 65;
 const float STAT_VEHICLESTAT_RELOAD2    = 66;
 const float STAT_VEHICLESTAT_W2MODE     = 67;
 // 68 empty?
-// 69 empty?
+const float STAT_NADE_TIMER             = 69;
 const float STAT_SECRETS_TOTAL          = 70;
 const float STAT_SECRETS_FOUND          = 71;
 const float STAT_RESPAWN_TIME           = 72;
@@ -81,8 +81,13 @@ const float STAT_WEAPONS3               = 75;
 const float STAT_MONSTERS_TOTAL         = 76;
 const float STAT_MONSTERS_KILLED        = 77;
 const float STAT_BUFFS                  = 78;
-// 79 empty?
-// 80 empty?
+const float STAT_NADE_BONUS             = 79;
+const float STAT_NADE_BONUS_TYPE        = 80;
+const float STAT_NADE_BONUS_SCORE       = 81;
+const float STAT_HEALING_ORB            = 82;
+const float STAT_HEALING_ORB_ALPHA      = 83;
+// 84 empty?
+// 85 empty?
 // 86 empty?
 // 87 empty?
 // 88 empty?
index 3df7c17630851ab470d8ca1350b434cf34237c02..4ad97139ff1e67dfab33625c1055971649436876 100644 (file)
@@ -801,10 +801,13 @@ string autocvar_g_forced_team_otherwise;
 string autocvar_g_forced_team_pink;
 string autocvar_g_forced_team_red;
 string autocvar_g_forced_team_yellow;
+float autocvar_g_freezetag_frozen_damage_trigger;
 float autocvar_g_freezetag_frozen_force;
 float autocvar_g_freezetag_frozen_maxtime;
 float autocvar_g_freezetag_revive_falldamage;
 float autocvar_g_freezetag_revive_falldamage_health;
+float autocvar_g_freezetag_revive_nade;
+float autocvar_g_freezetag_revive_nade_health;
 float autocvar_g_freezetag_point_leadlimit;
 float autocvar_g_freezetag_point_limit;
 float autocvar_g_freezetag_revive_extra_size;
@@ -1106,6 +1109,7 @@ float autocvar_sv_dodging_up_speed;
 float autocvar_sv_dodging_wall_distance_threshold;
 float autocvar_sv_dodging_wall_dodging;
 float autocvar_sv_dodging_frozen;
+float autocvar_sv_dodging_frozen_doubletap;
 float autocvar_sv_doublejump;
 float autocvar_sv_eventlog;
 float autocvar_sv_eventlog_console;
@@ -1268,6 +1272,8 @@ float autocvar_g_random_gravity_negative;
 float autocvar_g_random_gravity_delay;
 float autocvar_g_nades;
 float autocvar_g_nades_spawn;
+float autocvar_g_nades_spawn_count;
+float autocvar_g_nades_client_select;
 float autocvar_g_nades_nade_lifetime;
 float autocvar_g_nades_nade_minforce;
 float autocvar_g_nades_nade_maxforce;
@@ -1278,6 +1284,44 @@ float autocvar_g_nades_nade_edgedamage;
 float autocvar_g_nades_nade_radius;
 float autocvar_g_nades_nade_force;
 float autocvar_g_nades_nade_newton_style;
+float autocvar_g_nades_napalm_ball_count;
+float autocvar_g_nades_napalm_ball_spread;
+float autocvar_g_nades_napalm_ball_damage;
+float autocvar_g_nades_napalm_ball_damageforcescale;
+float autocvar_g_nades_napalm_ball_lifetime;
+float autocvar_g_nades_napalm_ball_radius;
+float autocvar_g_nades_napalm_blast;
+float autocvar_g_nades_napalm_fountain_lifetime;
+float autocvar_g_nades_napalm_fountain_delay;
+float autocvar_g_nades_napalm_fountain_radius;
+float autocvar_g_nades_napalm_fountain_damage;
+float autocvar_g_nades_napalm_fountain_edgedamage;
+float autocvar_g_nades_napalm_burntime;
+float autocvar_g_nades_napalm_selfdamage;
+float autocvar_g_nades_nade_type;
+float autocvar_g_nades_bonus_type;
+float autocvar_g_nades_bonus;
+float autocvar_g_nades_bonus_onstrength;
+float autocvar_g_nades_bonus_client_select;
+float autocvar_g_nades_bonus_max;
+float autocvar_g_nades_bonus_score_max;
+float autocvar_g_nades_bonus_score_time;
+float autocvar_g_nades_bonus_score_time_flagcarrier;
+float autocvar_g_nades_bonus_score_minor;
+float autocvar_g_nades_bonus_score_low;
+float autocvar_g_nades_bonus_score_high;
+float autocvar_g_nades_bonus_score_medium;
+float autocvar_g_nades_bonus_score_spree;
+float autocvar_g_nades_ice_freeze_time;
+float autocvar_g_nades_ice_health;
+float autocvar_g_nades_ice_explode;
+float autocvar_g_nades_ice_teamcheck;
+float autocvar_g_nades_heal_time;
+float autocvar_g_nades_heal_rate;
+float autocvar_g_nades_heal_friend;
+float autocvar_g_nades_heal_foe;
+string autocvar_g_nades_pokenade_monster_type;
+float autocvar_g_nades_pokenade_monster_lifetime;
 float autocvar_g_campcheck_damage;
 float autocvar_g_campcheck_distance;
 float autocvar_g_campcheck_interval;
index 3467e2b395d61c7c1f28357fe0460b0a97011394..61f4ab5e8f307b8bc421b4f250090c5169652c0e 100644 (file)
@@ -111,7 +111,7 @@ float bot_shouldattack(entity e)
                        return FALSE;
        }
 
-       if(e.freezetag_frozen)
+       if(e.frozen)
                return FALSE;
 
        // If neither player has ball then don't attack unless the ball is on the
index 4f6e29ceacd84ded7d04e289920bb067eee0c909..b3c6b65775d5121c8a6394cd199b51ff3b08363a 100644 (file)
@@ -156,6 +156,8 @@ void PutObserverInServer (void)
 
        Portal_ClearAll(self);
 
+       Unfreeze(self);
+
        if(self.alivetime)
        {
                if(!warmup_stage)
@@ -227,6 +229,7 @@ void PutObserverInServer (void)
        self.angles_z = 0;
        self.fixangle = TRUE;
        self.crouch = FALSE;
+       self.revival_time = 0;
 
        setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
        self.prevorigin = self.origin;
@@ -505,6 +508,7 @@ void PutClientInServer (void)
                self.punchvector = '0 0 0';
                self.oldvelocity = self.velocity;
                self.fire_endtime = -1;
+               self.revival_time = 0;
 
                entity spawnevent = spawn();
                spawnevent.owner = self;
@@ -579,6 +583,8 @@ void PutClientInServer (void)
                        activator = world;
                self = oldself;
 
+               Unfreeze(self);
+
                spawn_spot = spot;
                MUTATOR_CALLHOOK(PlayerSpawn);
 
@@ -928,7 +934,7 @@ void ClientKill (void)
 {
        if(gameover) return;
        if(self.player_blocked) return;
-       if(self.freezetag_frozen) return;
+       if(self.frozen) return;
 
        ClientKill_TeamChange(0);
 }
@@ -1257,6 +1263,8 @@ void ClientDisconnect (void)
 
        Portal_ClearAll(self);
 
+       Unfreeze(self);
+
        RemoveGrapplingHook(self);
 
        // Here, everything has been done that requires this player to be a client.
@@ -1557,6 +1565,7 @@ void player_regen (void)
        regen_mod_rot = rot_mod;
        regen_mod_limit = limit_mod;
        if(!MUTATOR_CALLHOOK(PlayerRegen))
+       if(!self.frozen)
        {
                float minh, mina, maxh, maxa, limith, limita;
                maxh = autocvar_g_balance_health_rotstable;
@@ -1705,6 +1714,8 @@ void SpectateCopy(entity spectatee) {
        self.dmg_inflictor = spectatee.dmg_inflictor;
        self.v_angle = spectatee.v_angle;
        self.angles = spectatee.v_angle;
+       self.frozen = spectatee.frozen;
+       self.revive_progress = spectatee.revive_progress;
        if(!self.BUTTON_USE)
                self.fixangle = TRUE;
        setorigin(self, spectatee.origin);
@@ -1910,6 +1921,7 @@ void LeaveSpectatorMode()
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
                {
                        self.classname = "player";
+                       nades_RemoveBonus(self);
 
                        if(autocvar_g_campaign || autocvar_g_balance_teams)
                                { JoinBestTeam(self, FALSE, TRUE); }
@@ -2215,6 +2227,30 @@ void PlayerPreThink (void)
                return;
 #endif
 
+       if(self.frozen == 2)
+       {
+               self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
+               self.health = max(1, self.revive_progress * start_health);
+               self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+
+               if(self.revive_progress >= 1)
+                       Unfreeze(self);
+       }
+       else if(self.frozen == 3)
+       {
+               self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
+               self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
+               
+               if(self.health < 1)
+               {
+                       if(self.vehicle)
+                               vehicles_exit(VHEF_RELESE);
+                       self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+               }
+               else if ( self.revive_progress <= 0 )
+                       Unfreeze(self);
+       }
+
        MUTATOR_CALLHOOK(PlayerPreThink);
 
        if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
@@ -2339,7 +2375,7 @@ void PlayerPreThink (void)
                        do_crouch = 0;
                if(self.vehicle)
                        do_crouch = 0;
-               if(self.freezetag_frozen)
+               if(self.frozen)
                        do_crouch = 0;
                if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
                        do_crouch = 0;
index 50518040b7ce07649d804af7504f15b1a2c87787..14747fa99aabe535d9074e7e03ae6391adcebf80 100644 (file)
@@ -19,6 +19,9 @@ When you press the jump key
 */
 void PlayerJump (void)
 {
+       if(self.frozen)
+               return; // no jumping in freezetag when frozen
+
        if(self.player_blocked)
                return; // no jumping while blocked
 
@@ -792,6 +795,28 @@ void SV_PlayerPhysics()
                self.stat_sv_airspeedlimit_nonqw *= 0.5;
        }
 
+       if(self.frozen)
+       {
+               if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
+               {
+                       self.movement_x = bound(-5, self.movement_x, 5);
+                       self.movement_y = bound(-5, self.movement_y, 5);
+                       self.movement_z = bound(-5, self.movement_z, 5);
+               }
+               else
+                       self.movement = '0 0 0';
+               self.disableclientprediction = 1;
+
+               vector midpoint = ((self.absmin + self.absmax) * 0.5);
+               if(pointcontents(midpoint) == CONTENT_WATER)
+               {
+                       self.velocity = self.velocity * 0.5;
+
+                       if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                               { self.velocity_z = 200; }
+               }
+       }
+
        MUTATOR_CALLHOOK(PlayerPhysics);
 
        if(self.player_blocked)
@@ -993,7 +1018,7 @@ void SV_PlayerPhysics()
                        PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
                }
        }
-       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen)
+       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.frozen)
        {
                //makevectors(self.v_angle_y * '0 1 0');
                makevectors(self.v_angle);
index 3a687eca81ca5aa9f358f7816df5d3b75e37b2d2..c11e92051074b7fcbf15b208b774f14d96c00d88 100644 (file)
@@ -246,7 +246,7 @@ void player_anim (void)
                else
                        deadbits = ANIMSTATE_DEAD2;
        float animbits = deadbits;
-       if(self.freezetag_frozen)
+       if(self.frozen)
                animbits |= ANIMSTATE_FROZEN;
        if(self.crouch)
                animbits |= ANIMSTATE_DUCK;
@@ -684,6 +684,9 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
 
                // when we get here, player actually dies
 
+               Unfreeze(self); // remove any icy remains
+               self.health = 0; // Unfreeze resets health, so we need to set it back
+
                // clear waypoints
                WaypointSprite_PlayerDead();
                // throw a weapon
index 1c05aca7858a64d141621a6b2c36e96e3b1eb7b7..41cdd2b05e289edff14ebd35f20fa8700056d287 100644 (file)
@@ -328,6 +328,8 @@ void W_ThrowWeapon(vector velo, vector delta, float doreduce)
        w = self.weapon;
        if (w == 0)
                return; // just in case
+       if(self.frozen)
+               return;
        if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
                return;
        if(!autocvar_g_weapon_throwable)
@@ -356,7 +358,7 @@ float forbidWeaponUse()
                return 1;
        if(self.player_blocked)
                return 1;
-       if(self.freezetag_frozen)
+       if(self.frozen)
                return 1;
        return 0;
 }
index 78424484aa34f4ff826c794a5c893383414c600f..51a04b59135e01e3ac0fd9b6bd62c1d25271651f 100644 (file)
@@ -289,7 +289,7 @@ void ClientCommand_mobspawn(float request, float argc)
                        else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; }
                        else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; }
                        else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; }
-                       else if(self.freezetag_frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
+                       else if(self.frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
                        else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; }
                        else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; }
                        else if(monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
index 4d208ebda808341f778f242ba0135d7255bd29d9..3c747bdbb2633c439df886e6b470da9aa82f5165 100644 (file)
@@ -48,6 +48,8 @@ float CSQCProjectile_SendEntity(entity to, float sf)
                        WriteByte(MSG_ENTITY, ft);
                        WriteByte(MSG_ENTITY, fr);
                }
+
+               WriteByte(MSG_ENTITY, self.realowner.team);
        }
 
        if(sf & 2)
index a972bbeacd9532aeab3bdb2d927a193f92f42589..dd0b5c22bb13792e6ffc89c4a98b302cbed6b02a 100644 (file)
@@ -583,7 +583,12 @@ float serverflags;
 
 .float player_blocked;
 
-.float freezetag_frozen;
+.float frozen; // for freeze attacks
+.float revive_progress;
+.float revival_time; // time at which player was last revived
+.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
+.entity iceblock;
+.entity frozen_by; // for ice fields
 
 .entity muzzle_flash;
 .float misc_bulletcounter;     // replaces uzi & hlac bullet counter.
index 7cc48be005590a39402254691d529d4b53b22a39..47443e6d70f34acfdc175d3a16efb71a6823ef18 100644 (file)
@@ -549,6 +549,86 @@ void Obituary(entity attacker, entity inflictor, entity targ, float deathtype)
        if(targ.killcount) { targ.killcount = 0; }
 }
 
+void Ice_Think()
+{
+       if(!self.owner.frozen || self.owner.iceblock != self)
+       {
+               remove(self);
+               return;
+       }
+       setorigin(self, self.owner.origin - '0 0 16');
+       self.nextthink = time;
+}
+
+void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint)
+{
+       if(!IS_PLAYER(targ) && !(targ.flags & FL_MONSTER)) // only specified entities can be freezed
+               return;
+
+       if(targ.frozen)
+               return;
+
+       float targ_maxhealth = ((targ.flags & FL_MONSTER) ? targ.max_health : start_health);
+
+       targ.frozen = frozen_type;
+       targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
+       targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
+       targ.revive_speed = freeze_time;
+
+       entity ice, head;
+       ice = spawn();
+       ice.owner = targ;
+       ice.classname = "ice";
+       ice.scale = targ.scale;
+       ice.think = Ice_Think;
+       ice.nextthink = time;
+       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       setmodel(ice, "models/ice/ice.md3");
+       ice.alpha = 1;
+       ice.colormod = Team_ColorRGB(targ.team);
+       ice.glowmod = ice.colormod;
+       targ.iceblock = ice;
+       targ.revival_time = 0;
+
+       entity oldself;
+       oldself = self;
+       self = ice;
+       Ice_Think();
+       self = oldself;
+
+       RemoveGrapplingHook(targ);
+
+       FOR_EACH_PLAYER(head)
+       if(head.hook.aiment == targ)
+               RemoveGrapplingHook(head);
+
+       // add waypoint
+       if(show_waypoint)       
+               WaypointSprite_Spawn("frozen", 0, 0, targ, '0 0 64', world, targ.team, targ, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
+}
+
+void Unfreeze (entity targ)
+{
+       if(targ.frozen && targ.frozen != 3) // only reset health if target was frozen
+               targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
+
+       entity head;
+       targ.frozen = 0;
+       targ.revive_progress = 0;
+       targ.revival_time = time;
+       
+       WaypointSprite_Kill(targ.waypointsprite_attached);
+       
+       FOR_EACH_PLAYER(head)
+       if(head.hook.aiment == targ)
+               RemoveGrapplingHook(head);
+
+       // remove the ice block
+       if(targ.iceblock)
+               remove(targ.iceblock);
+       targ.iceblock = world;
+}
+
 // these are updated by each Damage call for use in button triggering and such
 entity damage_targ;
 entity damage_inflictor;
@@ -690,7 +770,63 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                mirrordamage = frag_mirrordamage;
                force = frag_force;
 
-               if (!g_minstagib)
+               if(targ.frozen)
+               if(deathtype != DEATH_HURTTRIGGER && deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_AUTOTEAMCHANGE)
+               {
+                       if(autocvar_g_freezetag_revive_falldamage > 0)
+                       if(deathtype == DEATH_FALL)
+                       if(damage >= autocvar_g_freezetag_revive_falldamage)
+                       {
+                               Unfreeze(targ);
+                               targ.health = autocvar_g_freezetag_revive_falldamage_health;
+                               pointparticles(particleeffectnum("iceorglass"), targ.origin, '0 0 0', 3);
+                               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
+                               Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+                       }
+
+                       damage = 0;
+                       force *= autocvar_g_freezetag_frozen_force;
+               }
+               
+               if(targ.frozen && deathtype == DEATH_HURTTRIGGER && !autocvar_g_freezetag_frozen_damage_trigger)
+               {
+                       pointparticles(particleeffectnum("teleport"), targ.origin, '0 0 0', 1);
+               
+                       entity oldself = self;
+                       self = targ;
+                       entity spot = SelectSpawnPoint (FALSE);
+                       
+                       if(spot)
+                       {
+                               damage = 0;
+                               self.deadflag = DEAD_NO;
+
+                               self.angles = spot.angles;
+                               
+                               self.effects = 0;
+                               self.effects |= EF_TELEPORT_BIT;
+
+                               self.angles_z = 0; // never spawn tilted even if the spot says to
+                               self.fixangle = TRUE; // turn this way immediately
+                               self.velocity = '0 0 0';
+                               self.avelocity = '0 0 0';
+                               self.punchangle = '0 0 0';
+                               self.punchvector = '0 0 0';
+                               self.oldvelocity = self.velocity;
+                               
+                               self.spawnorigin = spot.origin;
+                               setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
+                               // don't reset back to last position, even if new position is stuck in solid
+                               self.oldorigin = self.origin;
+                               self.prevorigin = self.origin;
+                               
+                               pointparticles(particleeffectnum("teleport"), self.origin, '0 0 0', 1);
+                       }
+                       
+                       self = oldself;
+               }
+
+               if(!g_minstagib)
                {
                        // apply strength multiplier
                        if (attacker.items & IT_STRENGTH)
@@ -1195,7 +1331,7 @@ void Fire_ApplyDamage(entity e)
                e.fire_endtime = 0;
 
        // ice stops fire
-       if(e.freezetag_frozen)
+       if(e.frozen)
                e.fire_endtime = 0;
 
        t = min(frametime, e.fire_endtime - time);
@@ -1212,7 +1348,7 @@ void Fire_ApplyDamage(entity e)
        e.fire_hitsound = TRUE;
 
        if (!IS_INDEPENDENT_PLAYER(e))
-       if(!e.freezetag_frozen)
+       if(!e.frozen)
        FOR_EACH_PLAYER(other) if(e != other)
        {
                if(IS_PLAYER(other))
index 324d332887c07161aa39a16449240432dcfb77d9..a1460ded8aa0e5bf6f19952754ba83d3213ee5be 100644 (file)
@@ -795,6 +795,10 @@ void spawnfunc_worldspawn (void)
 
        addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
 
+       // freeze attacks
+       addstat(STAT_FROZEN, AS_INT, frozen);
+       addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
+
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
index 64760a994eeb37363795bc6086f47a04e30f79a9..55e01b2453f1672fb72dcd9e68923d1641b9d920 100644 (file)
@@ -1268,6 +1268,7 @@ void precache()
 {
     // gamemode related things
     precache_model ("models/misc/chatbubble.spr");
+       precache_model("models/ice/ice.md3");
 
 #ifdef TTURRETS_ENABLED
     if (autocvar_g_turrets)
index 8a6315c4236bd1673758253449af1889fe2e35e8..8b38ceb096a8cd537eda2238aad552d6fb7ae103 100644 (file)
@@ -67,12 +67,15 @@ float CA_GetWinnerTeam()
 #define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
 float CA_CheckWinner()
 {
+       entity e;
        if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
        {
                Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
                allowed_to_spawn = FALSE;
                round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+               FOR_EACH_PLAYER(e)
+                       nades_Clear(e);
                return 1;
        }
 
@@ -95,6 +98,10 @@ float CA_CheckWinner()
 
        allowed_to_spawn = FALSE;
        round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+
+       FOR_EACH_PLAYER(e)
+               nades_Clear(e);
+
        return 1;
 }
 
index c9c9b4584b14af089b437c5b81f4c26e105dae95..967d8d656fdf72b43a54e7fdff077c3d111c4d50 100644 (file)
@@ -388,6 +388,8 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
 
        if (!player) { return; } // without someone to give the reward to, we can't possibly cap
 
+       nades_GiveBonus(player, autocvar_g_nades_bonus_score_high );
+
        // messages and sounds
        Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_));
        ctf_CaptureRecord(enemy_flag, player);
@@ -448,6 +450,8 @@ void ctf_Handle_Return(entity flag, entity player)
        {
                PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
                PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+
+               nades_GiveBonus(player,autocvar_g_nades_bonus_score_medium);
        }
 
        TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it
@@ -500,6 +504,7 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
 
        // scoring
        PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+       nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor);
        switch(pickuptype)
        {
                case PICKUP_BASE:
@@ -791,7 +796,8 @@ void ctf_FlagTouch()
        }
 
        // special touch behaviors
-       if(toucher.vehicle_flags & VHF_ISVEHICLE)
+       if(toucher.frozen) { return; }
+       else if(toucher.vehicle_flags & VHF_ISVEHICLE)
        {
                if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
                        toucher = toucher.owner; // the player is actually the vehicle owner, not other
index 26e19910ad0f3f571a19f779bec902872b9de8e7..ffdc1612dde1235608c2874da85eefc60dd37c15 100644 (file)
@@ -1,7 +1,6 @@
 .float freezetag_frozen_time;
 .float freezetag_frozen_timeout;
 .float freezetag_revive_progress;
-.entity freezetag_ice;
 #define ICE_MAX_ALPHA 1
 #define ICE_MIN_ALPHA 0.1
 float freezetag_teams;
@@ -10,29 +9,18 @@ void freezetag_count_alive_players()
 {
        entity e;
        total_players = redalive = bluealive = yellowalive = pinkalive = 0;
-       FOR_EACH_PLAYER(e) {
-               if(e.team == NUM_TEAM_1 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++redalive;
-               }
-               else if(e.team == NUM_TEAM_2 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++bluealive;
-               }
-               else if(e.team == NUM_TEAM_3 && e.health >= 1)
-               {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++yellowalive;
-               }
-               else if(e.team == NUM_TEAM_4 && e.health >= 1)
+       FOR_EACH_PLAYER(e)
+       {
+               switch(e.team)
                {
-                       ++total_players;
-                       if (!e.freezetag_frozen) ++pinkalive;
+                       case NUM_TEAM_1: ++total_players; if(e.health >= 1 && e.frozen != 1) ++redalive; break;
+                       case NUM_TEAM_2: ++total_players; if(e.health >= 1 && e.frozen != 1) ++bluealive; break;
+                       case NUM_TEAM_3: ++total_players; if(e.health >= 1 && e.frozen != 1) ++yellowalive; break;
+                       case NUM_TEAM_4: ++total_players; if(e.health >= 1 && e.frozen != 1) ++pinkalive; break;
                }
        }
-       FOR_EACH_REALCLIENT(e) {
+       FOR_EACH_REALCLIENT(e)
+       {
                e.redalive_stat = redalive;
                e.bluealive_stat = bluealive;
                e.yellowalive_stat = yellowalive;
@@ -102,7 +90,7 @@ float freezetag_CheckWinner()
                FOR_EACH_PLAYER(e)
                {
                        e.freezetag_frozen_timeout = 0;
-                       e.freezetag_revive_progress = 0;
+                       nades_Clear(e);
                }
                round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
                return 1;
@@ -128,21 +116,12 @@ float freezetag_CheckWinner()
        FOR_EACH_PLAYER(e)
        {
                e.freezetag_frozen_timeout = 0;
-               e.freezetag_revive_progress = 0;
+               nades_Clear(e);
        }
        round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
        return 1;
 }
 
-// this is needed to allow the player to turn his view around (fixangle can't
-// be used to freeze his view, as that also changes the angles), while not
-// turning that ice object with the player
-void freezetag_Ice_Think()
-{
-       setorigin(self, self.owner.origin - '0 0 16');
-       self.nextthink = time;
-}
-
 void freezetag_Add_Score(entity attacker)
 {
        if(attacker == self)
@@ -163,51 +142,25 @@ void freezetag_Add_Score(entity attacker)
 
 void freezetag_Freeze(entity attacker)
 {
-       if(self.freezetag_frozen)
+       if(self.frozen)
                return;
-       self.freezetag_frozen = 1;
-       self.freezetag_frozen_time = time;
-       self.freezetag_revive_progress = 0;
-       self.health = 1;
+
        if(autocvar_g_freezetag_frozen_maxtime > 0)
                self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
 
-       freezetag_count_alive_players();
+       Freeze(self, 0, 1, TRUE);
 
-       entity ice;
-       ice = spawn();
-       ice.owner = self;
-       ice.classname = "freezetag_ice";
-       ice.think = freezetag_Ice_Think;
-       ice.nextthink = time;
-       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
-       ice.alpha = ICE_MAX_ALPHA;
-       ice.colormod = Team_ColorRGB(self.team);
-       ice.glowmod = ice.colormod;
-       setmodel(ice, "models/ice/ice.md3");
-
-       self.freezetag_ice = ice;
-
-       RemoveGrapplingHook(self);
-
-       // add waypoint
-       WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
+       freezetag_count_alive_players();
 
        freezetag_Add_Score(attacker);
 }
 
 void freezetag_Unfreeze(entity attacker)
 {
-       self.freezetag_frozen = 0;
        self.freezetag_frozen_time = 0;
        self.freezetag_frozen_timeout = 0;
-       self.freezetag_revive_progress = 0;
 
-       remove(self.freezetag_ice);
-       self.freezetag_ice = world;
-
-       if(self.waypointsprite_attached)
-               WaypointSprite_Kill(self.waypointsprite_attached);
+       Unfreeze(self);
 }
 
 
@@ -227,7 +180,7 @@ void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradiu
        {
                if ((head != self) && (head.team == self.team))
                {
-                       if (head.freezetag_frozen)
+                       if (head.frozen == 1)
                        {
                                distance = vlen(head.origin - org);
                                if (distance > sradius)
@@ -259,12 +212,12 @@ void havocbot_role_ft_offense()
        unfrozen = 0;
        FOR_EACH_PLAYER(head)
        {
-               if ((head.team == self.team) && (!head.freezetag_frozen))
+               if ((head.team == self.team) && (head.frozen != 1))
                        unfrozen++;
        }
 
        // If only one left on team or if role has timed out then start trying to free players.
-       if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
+       if (((unfrozen == 0) && (!self.frozen)) || (time > self.havocbot_role_timeout))
        {
                dprint("changing role to freeing\n");
                self.havocbot_role = havocbot_role_ft_freeing;
@@ -332,7 +285,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
        {
-               if(self.freezetag_frozen)
+               if(self.frozen)
                        freezetag_Unfreeze(world);
                freezetag_count_alive_players();
                return 1; // let the player die so that he can respawn whenever he wants
@@ -344,18 +297,19 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
                || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
        {
                // let the player die, he will be automatically frozen when he respawns
-               if(!self.freezetag_frozen)
+               if(self.frozen != 1)
                {
                        freezetag_Add_Score(frag_attacker);
                        freezetag_count_alive_players();
                }
                else
                        freezetag_Unfreeze(world); // remove ice
+               self.health = 0; // Unfreeze resets health
                self.freezetag_frozen_timeout = -2; // freeze on respawn
                return 1;
        }
 
-       if(self.freezetag_frozen)
+       if(self.frozen)
                return 1;
 
        freezetag_Freeze(frag_attacker);
@@ -375,8 +329,6 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
                Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
        }
 
-       frag_target.health = 1; // "respawn" the player :P
-
        return 1;
 }
 
@@ -408,8 +360,6 @@ MUTATOR_HOOKFUNCTION(freezetag_reset_map_players)
        FOR_EACH_PLAYER(self)
        {
                self.killcount = 0;
-               if (self.freezetag_frozen)
-                       freezetag_Unfreeze(world);
                self.freezetag_frozen_timeout = -1;
                PutClientInServer();
                self.freezetag_frozen_timeout = 0;
@@ -432,7 +382,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
        if(gameover)
                return 1;
 
-       if(self.freezetag_frozen)
+       if(self.frozen == 1)
        {
                // keep health = 1
                self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
@@ -444,8 +394,9 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
 
        entity o;
        o = world;
-       if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
-               self.freezetag_ice.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
+       //if(self.frozen)
+       //if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
+               //self.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
 
        if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
                n = -1;
@@ -453,34 +404,27 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
        {
                vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
                n = 0;
-               FOR_EACH_PLAYER(other) if(self != other)
+               FOR_EACH_PLAYER(other)
+               if(self != other)
+               if(other.frozen == 0)
+               if(other.deadflag == DEAD_NO)
+               if(SAME_TEAM(other, self))
+               if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
                {
-                       if(other.freezetag_frozen == 0)
-                       {
-                               if(other.team == self.team)
-                               {
-                                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
-                                       {
-                                               if(!o)
-                                                       o = other;
-                                               if(self.freezetag_frozen)
-                                                       other.reviving = TRUE;
-                                               ++n;
-                                       }
-                               }
-                       }
+                       if(!o)
+                               o = other;
+                       if(self.frozen == 1)
+                               other.reviving = TRUE;
+                       ++n;
                }
        }
 
-       if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
+       if(n && self.frozen == 1) // OK, there is at least one teammate reviving us
        {
-               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
-               if(warmup_stage)
-                       self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
-               else
-                       self.health = max(1, self.freezetag_revive_progress * start_health);
+               self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+               self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
 
-               if(self.freezetag_revive_progress >= 1)
+               if(self.revive_progress >= 1)
                {
                        freezetag_Unfreeze(self);
                        freezetag_count_alive_players();
@@ -499,6 +443,8 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
                                {
                                        PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
                                        PlayerScore_Add(other, SP_SCORE, +1);
+
+                                       nades_GiveBonus(other,autocvar_g_nades_bonus_score_low);
                                }
                        }
 
@@ -511,88 +457,24 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
                {
                        if(other.reviving)
                        {
-                               other.freezetag_revive_progress = self.freezetag_revive_progress;
+                               other.revive_progress = self.revive_progress;
                                other.reviving = FALSE;
                        }
                }
        }
-       else if(!n && self.freezetag_frozen) // only if no teammate is nearby will we reset
-       {
-               self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
-               if(warmup_stage)
-                       self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
-               else
-                       self.health = max(1, self.freezetag_revive_progress * start_health);
-       }
-       else if(!n)
+       else if(!n && self.frozen == 1) // only if no teammate is nearby will we reset
        {
-               self.freezetag_revive_progress = 0; // thawing nobody
+               self.revive_progress = bound(0, self.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
+               self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
        }
-
-       return 1;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
-{
-       if(self.freezetag_frozen)
+       else if(!n && !self.frozen)
        {
-               if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
-               {
-                       self.movement_x = bound(-5, self.movement_x, 5);
-                       self.movement_y = bound(-5, self.movement_y, 5);
-                       self.movement_z = bound(-5, self.movement_z, 5);
-               }
-               else
-                       self.movement = '0 0 0';
-
-               self.disableclientprediction = 1;
+               self.revive_progress = 0; // thawing nobody
        }
-       return 1;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
-{
-       if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
-       {
-               if(autocvar_g_freezetag_revive_falldamage > 0)
-               if(frag_deathtype == DEATH_FALL)
-               if(frag_damage >= autocvar_g_freezetag_revive_falldamage)
-               {
-                       freezetag_Unfreeze(frag_target);
-                       frag_target.health = autocvar_g_freezetag_revive_falldamage_health;
-                       pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
-                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, frag_target.netname);
-                       Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_FALL);
-               }
 
-               frag_damage = 0;
-               frag_force = frag_force * autocvar_g_freezetag_frozen_force;
-       }
        return 1;
 }
 
-MUTATOR_HOOKFUNCTION(freezetag_PlayerJump)
-{
-       if(self.freezetag_frozen)
-               return TRUE; // no jumping in freezetag when frozen
-
-       return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
-{
-       if (self.freezetag_frozen)
-               return 1;
-       return 0;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
-{
-       if (other.freezetag_frozen)
-               return MUT_ITEMTOUCH_RETURN;
-       return MUT_ITEMTOUCH_CONTINUE;
-}
-
 MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
 {
        if (!self.deadflag)
@@ -606,31 +488,14 @@ MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
        return TRUE;
 }
 
-MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
-{
-       self.freezetag_frozen = other.freezetag_frozen;
-       self.freezetag_revive_progress = other.freezetag_revive_progress;
-       return 0;
-}
-
 MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
 {
        ret_float = freezetag_teams;
        return 0;
 }
 
-MUTATOR_HOOKFUNCTION(freezetag_VehicleTouch)
-{
-       if(other.freezetag_frozen)
-               return TRUE;
-
-       return FALSE;
-}
-
 void freezetag_Initialize()
 {
-       precache_model("models/ice/ice.md3");
-
        freezetag_teams = autocvar_g_freezetag_teams_override;
        if(freezetag_teams < 2)
                freezetag_teams = autocvar_g_freezetag_teams;
@@ -644,9 +509,6 @@ void freezetag_Initialize()
        addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
        addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
        addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
-
-       addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
-       addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
 }
 
 MUTATOR_DEFINITION(gamemode_freezetag)
@@ -658,15 +520,8 @@ MUTATOR_DEFINITION(gamemode_freezetag)
        MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
        MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
-       MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
-       MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerJump, freezetag_PlayerJump, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
        MUTATOR_HOOK(HavocBot_ChooseRole, freezetag_BotRoles, CBC_ORDER_ANY);
-       MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
        MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
-       MUTATOR_HOOK(VehicleTouch, freezetag_VehicleTouch, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
index 79058b0cf0c806ec98f1d1796bc1c9eb3b83873d..7e1006ec1d92252e624fda40366e08dee47feb99 100644 (file)
@@ -71,6 +71,7 @@ void ka_TouchEvent() // runs any time that the ball comes in contact with someth
                return;
        }
        if(other.deadflag != DEAD_NO) { return; }
+       if(other.frozen) { return; }
        if (!IS_PLAYER(other))
        {  // The ball just touched an object, most likely the world
                pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
index 34d87f97bb677aaaa3c87c99a1b4a808938df8f3..ab67a4821259b27115be5a9e89c2001a9d3d04c2 100644 (file)
@@ -500,6 +500,7 @@ void kh_WinnerTeam(float teem)  // runs when a team wins // Samual: Teem?.... TE
                f = DistributeEvenly_Get(1);
                kh_Scores_Event(key.owner, key, "capture", f, 0);
                PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1);
+               nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high);
        }
 
        first = TRUE;
index 014d37ec20169fd6497b6f3b50764b51deb1d91c..8695ca31b2b95b13103f2735d46a0141ffc18a86 100644 (file)
@@ -288,7 +288,7 @@ void basketball_touch(void)
                football_touch();
                return;
        }
-       if(!self.cnt && IS_PLAYER(other) && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
+       if(!self.cnt && IS_PLAYER(other) && !other.frozen && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
        {
                if(other.health <= 0)
                        return;
index c71fe60ea9bf3076107fa829438df4957af09db6..5aa534c33367726ce895f99f114193087ac207da 100644 (file)
@@ -107,7 +107,7 @@ void buff_Touch()
        }
 
        if((self.team && DIFF_TEAM(other, self))
-       || (other.freezetag_frozen)
+       || (other.frozen)
        || (other.vehicle)
        || (!IS_PLAYER(other))
        || (!self.buff_active)
@@ -200,7 +200,7 @@ void buff_Think()
     }
 
        if(!self.buff_active && !self.buff_activetime)
-       if(!self.owner || self.owner.freezetag_frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+       if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
        {
                buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
                self.owner = world;
@@ -419,7 +419,7 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate)
        if(frag_target.deadflag == DEAD_NO)
        if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER))
        if(frag_attacker != frag_target)
-       if(!frag_target.freezetag_frozen)
+       if(!frag_target.frozen)
        if(frag_target.takedamage)
        if(DIFF_TEAM(frag_attacker, frag_target))
                frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
@@ -570,7 +570,7 @@ MUTATOR_HOOKFUNCTION(buffs_PlayerThink)
                self.buff_disability_effect_time = time + 0.5;
        }
        
-       if(self.freezetag_frozen)
+       if(self.frozen)
        {
                if(self.buffs)
                {
index fb20d1cff2ea83e829708ba30bd999f2518bb7b5..2ec584db4cb23d265c5b105aceb2dbf6a3f33c7b 100644 (file)
@@ -25,6 +25,7 @@ MUTATOR_HOOKFUNCTION(campcheck_PlayerThink)
 {
        if(IS_PLAYER(self))
        if(self.deadflag == DEAD_NO)
+       if(!self.frozen)
        if(autocvar_g_campcheck_interval)
        {
                vector dist;
index 3f808499a8d4a5bd015b28ceec3a86d62797b3b2..b26fe1b9fff657fa13c38d35689ee689153d7c51 100644 (file)
@@ -35,7 +35,7 @@ MUTATOR_HOOKFUNCTION(dodging_PlayerPhysics) {
        float clean_up_and_do_nothing;
        float horiz_speed = autocvar_sv_dodging_horiz_speed;
 
-       if(self.freezetag_frozen)
+       if(self.frozen)
                horiz_speed = autocvar_sv_dodging_horiz_speed_frozen;
 
     if (self.deadflag != DEAD_NO)
@@ -169,8 +169,9 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
        tap_direction_x = 0;
        tap_direction_y = 0;
 
-       float frozen_dodging;
-       frozen_dodging = (self.freezetag_frozen && autocvar_sv_dodging_frozen);
+       float frozen_dodging, frozen_no_doubletap;
+       frozen_dodging = (self.frozen && autocvar_sv_dodging_frozen);
+       frozen_no_doubletap = (frozen_dodging && !autocvar_sv_dodging_frozen_doubletap);
 
        float dodge_detected;
        if (g_dodging == 0)
@@ -188,7 +189,7 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
 
        if (self.movement_x > 0) {
                // is this a state change?
-               if (!(self.pressedkeys & KEY_FORWARD) || frozen_dodging) {
+               if (!(self.pressedkeys & KEY_FORWARD) || frozen_no_doubletap) {
                        if ((time - self.last_FORWARD_KEY_time) < self.cvar_cl_dodging_timeout) {
                                tap_direction_x = 1.0;
                                dodge_detected = 1;
@@ -199,7 +200,7 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
 
        if (self.movement_x < 0) {
                // is this a state change?
-               if (!(self.pressedkeys & KEY_BACKWARD) || frozen_dodging) {
+               if (!(self.pressedkeys & KEY_BACKWARD) || frozen_no_doubletap) {
                        tap_direction_x = -1.0;
                        if ((time - self.last_BACKWARD_KEY_time) < self.cvar_cl_dodging_timeout)        {
                                dodge_detected = 1;
@@ -210,7 +211,7 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
 
        if (self.movement_y > 0) {
                // is this a state change?
-               if (!(self.pressedkeys & KEY_RIGHT) || frozen_dodging) {
+               if (!(self.pressedkeys & KEY_RIGHT) || frozen_no_doubletap) {
                        tap_direction_y = 1.0;
                        if ((time - self.last_RIGHT_KEY_time) < self.cvar_cl_dodging_timeout)   {
                                dodge_detected = 1;
@@ -221,7 +222,7 @@ MUTATOR_HOOKFUNCTION(dodging_GetPressedKeys) {
 
        if (self.movement_y < 0) {
                // is this a state change?
-               if (!(self.pressedkeys & KEY_LEFT) || frozen_dodging) {
+               if (!(self.pressedkeys & KEY_LEFT) || frozen_no_doubletap) {
                        tap_direction_y = -1.0;
                        if ((time - self.last_LEFT_KEY_time) < self.cvar_cl_dodging_timeout)    {
                                dodge_detected = 1;
index 6cce15211587a9f33ba1573b26e88c0eac99ace8..20811ad50815a4906ef0a37eb5c9a95de5f6111b 100644 (file)
@@ -225,7 +225,6 @@ MUTATOR_HOOKFUNCTION(minstagib_SplitHealthArmor)
 MUTATOR_HOOKFUNCTION(minstagib_ForbidThrowing)
 {
        // weapon dropping on death handled by FilterItem
-       nades_CheckThrow();
 
        return TRUE;
 }
index 2fad6022673c855e4fae529dfe8dc6158bba0954..da75e25ac23679fdaeea6bab190f0bc0311b2d62 100644 (file)
@@ -1,31 +1,20 @@
+.entity nade_spawnloc;
+
 void nade_timer_think()
 {
        self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
        self.nextthink = time;
        if(!self.owner || wasfreed(self.owner))
                remove(self);
-
 }
 
 void nade_burn_spawn(entity _nade)
 {
-       float p;
-
-       switch(_nade.realowner.team)
-       {
-               case NUM_TEAM_1: p = PROJECTILE_NADE_RED_BURN; break;
-               case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE_BURN; break;
-               case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW_BURN; break;
-               case NUM_TEAM_4: p = PROJECTILE_NADE_PINK_BURN; break;
-               default:                 p = PROJECTILE_NADE_BURN; break;
-       }
-
-       CSQCProjectile(_nade, TRUE, p, TRUE);
+       CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, TRUE), TRUE);
 }
 
 void nade_spawn(entity _nade)
 {
-       float p;
        entity timer = spawn();
        setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
        setattachment(timer, _nade, "");
@@ -38,47 +27,529 @@ void nade_spawn(entity _nade)
        timer.owner = _nade;
        timer.skin = 10;
 
-       switch(_nade.realowner.team)
+       _nade.effects |= EF_LOWPRECISION;
+
+       CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, FALSE), TRUE);
+}
+
+void napalm_damage(float dist, float damage, float edgedamage, float burntime)
+{
+       entity e;
+       float d;
+       vector p;
+
+       if ( damage < 0 )
+               return;
+
+       RandomSelection_Init();
+       for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
+               if(e.takedamage == DAMAGE_AIM)
+               if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
+               if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+               if(!e.frozen)
+               {
+                       p = e.origin;
+                       p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
+                       p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
+                       p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
+                       d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
+                       if(d < dist)
+                       {
+                               e.fireball_impactvec = p;
+                               RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+                       }
+               }
+       if(RandomSelection_chosen_ent)
        {
-               case NUM_TEAM_1: p = PROJECTILE_NADE_RED; break;
-               case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE; break;
-               case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW; break;
-               case NUM_TEAM_4: p = PROJECTILE_NADE_PINK; break;
-               default:                 p = PROJECTILE_NADE; break;
+               d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+               d = damage + (edgedamage - damage) * (d / dist);
+               Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+               //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+               pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
        }
+}
 
-       CSQCProjectile(_nade, TRUE, p, TRUE);
 
+void napalm_ball_think()
+{
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+
+       if(time > self.pushltime)
+       {
+               remove(self);
+               return;
+       }
+
+       vector midpoint = ((self.absmin + self.absmax) * 0.5);
+       if(pointcontents(midpoint) == CONTENT_WATER)
+       {
+               self.velocity = self.velocity * 0.5;
+
+               if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       { self.velocity_z = 200; }
+       }
+
+       self.angles = vectoangles(self.velocity);
+
+       napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
+                                 autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
+
+       self.nextthink = time + 0.1;
+}
+
+
+void nade_napalm_ball()
+{
+       entity proj;
+       vector kick;
+
+       spamsound(self, CH_SHOTS, "weapons/fireball_fire.wav", VOL_BASE, ATTEN_NORM);
+
+       proj = spawn ();
+       proj.owner = self.owner;
+       proj.realowner = self.realowner;
+       proj.team = self.owner.team;
+       proj.classname = "grenade";
+       proj.bot_dodge = TRUE;
+       proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
+       proj.movetype = MOVETYPE_BOUNCE;
+       proj.projectiledeathtype = DEATH_NADE_NAPALM;
+       PROJECTILE_MAKETRIGGER(proj);
+       setmodel(proj, "null");
+       proj.scale = 1;//0.5;
+       setsize(proj, '-4 -4 -4', '4 4 4');
+       setorigin(proj, self.origin);
+       proj.think = napalm_ball_think;
+       proj.nextthink = time;
+       proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale;
+       proj.effects = EF_LOWPRECISION | EF_FLAME;
+
+       kick_x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+       kick_y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread;
+       kick_z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread;
+       proj.velocity = kick;
+
+       proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime;
+
+       proj.angles = vectoangles(proj.velocity);
+       proj.flags = FL_PROJECTILE;
+       proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
+
+       //CSQCProjectile(proj, TRUE, PROJECTILE_NAPALM_FIRE, TRUE);
+}
+
+
+void napalm_fountain_think()
+{
+
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+
+       if(time >= self.ltime)
+       {
+               remove(self);
+               return;
+       }
+
+       vector midpoint = ((self.absmin + self.absmax) * 0.5);
+       if(pointcontents(midpoint) == CONTENT_WATER)
+       {
+               self.velocity = self.velocity * 0.5;
+
+               if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+                       { self.velocity_z = 200; }
+
+               UpdateCSQCProjectile(self);
+       }
+
+       napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
+               autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
+
+       self.nextthink = time + 0.1;
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
+               nade_napalm_ball();
+       }
+}
+
+void nade_napalm_boom()
+{
+       entity fountain;
+       local float c;
+       for (c = 0; c < autocvar_g_nades_napalm_ball_count; c ++)
+               nade_napalm_ball();
+
+
+       fountain = spawn();
+       fountain.owner = self.owner;
+       fountain.realowner = self.realowner;
+       fountain.origin = self.origin;
+       setorigin(fountain, fountain.origin);
+       fountain.think = napalm_fountain_think;
+       fountain.nextthink = time;
+       fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
+       fountain.pushltime = fountain.ltime;
+       fountain.team = self.team;
+       fountain.movetype = MOVETYPE_TOSS;
+       fountain.projectiledeathtype = DEATH_NADE_NAPALM;
+       fountain.bot_dodge = TRUE;
+       fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage;
+       fountain.nade_special_time = time;
+       setsize(fountain, '-16 -16 -16', '16 16 16');
+       CSQCProjectile(fountain, TRUE, PROJECTILE_NAPALM_FOUNTAIN, TRUE);
+}
+
+void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time)
+{
+       frost_target.frozen_by = freezefield.realowner;
+       pointparticles(particleeffectnum("electro_impact"), frost_target.origin, '0 0 0', 1);
+       Freeze(frost_target, 1/freeze_time, 3, FALSE);
+       if(frost_target.ballcarried)
+       if(g_keepaway) { ka_DropEvent(frost_target); }
+       else { DropBall(frost_target.ballcarried, frost_target.origin, frost_target.velocity);}
+       if(frost_target.flagcarried) { ctf_Handle_Throw(frost_target, world, DROP_THROW); }
+       if(frost_target.nade) { toss_nade(frost_target, '0 0 0', time + 0.05); }
+       
+       kh_Key_DropAll(frost_target, FALSE);
+}
+
+void nade_ice_think()
+{
+
+       if(round_handler_IsActive())
+       if(!round_handler_IsRoundStarted())
+       {
+               remove(self);
+               return;
+       }
+
+       if(time >= self.ltime)
+       {
+               if ( autocvar_g_nades_ice_explode )
+               {
+                       string expef;
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "nade_red_explode"; break;
+                               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+                               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+                               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+                               default:                 expef = "nade_neutral_explode"; break;
+                       }
+                       pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
+                       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+
+                       RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+                               autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+                       Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+                               autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+               }
+               remove(self);
+               return;
+       }
+
+
+       self.nextthink = time+0.1;
+
+       // gaussian
+       float randomr;
+       randomr = random();
+       randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
+       float randomw;
+       randomw = random()*M_PI*2;
+       vector randomp;
+       randomp_x = randomr*cos(randomw);
+       randomp_y = randomr*sin(randomw);
+       randomp_z = 1;
+       pointparticles(particleeffectnum("electro_muzzleflash"), self.origin + randomp, '0 0 0', 1);
+
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time+0.7;
+
+
+               pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
+               pointparticles(particleeffectnum("icefield"), self.origin, '0 0 0', 1);
+       }
+
+
+       float current_freeze_time = self.ltime - time - 0.1;
+
+       entity e;
+       for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
+       if(e != self)
+       if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
+       if(e.takedamage && e.deadflag == DEAD_NO)
+       if(e.health > 0)
+       if(!e.revival_time || ((time - e.revival_time) >= 1.5))
+       if(!e.frozen)
+       if(current_freeze_time > 0)
+               nade_ice_freeze(self, e, current_freeze_time);
+}
+
+void nade_ice_boom()
+{
+       entity fountain;
+       fountain = spawn();
+       fountain.owner = self.owner;
+       fountain.realowner = self.realowner;
+       fountain.origin = self.origin;
+       setorigin(fountain, fountain.origin);
+       fountain.think = nade_ice_think;
+       fountain.nextthink = time;
+       fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
+       fountain.pushltime = fountain.wait = fountain.ltime;
+       fountain.team = self.team;
+       fountain.movetype = MOVETYPE_TOSS;
+       fountain.projectiledeathtype = DEATH_NADE_ICE;
+       fountain.bot_dodge = FALSE;
+       setsize(fountain, '-16 -16 -16', '16 16 16');
+       fountain.nade_special_time = time+0.3;
+       fountain.angles = self.angles;
+
+       if ( autocvar_g_nades_ice_explode )
+       {
+               setmodel(fountain, "models/grenademodel.md3");
+               entity timer = spawn();
+               setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
+               setattachment(timer, fountain, "");
+               timer.classname = "nade_timer";
+               timer.colormap = self.colormap;
+               timer.glowmod = self.glowmod;
+               timer.think = nade_timer_think;
+               timer.nextthink = time;
+               timer.wait = fountain.ltime;
+               timer.owner = fountain;
+               timer.skin = 10;
+       }
+       else
+               setmodel(fountain, "null");
+}
+
+void nade_translocate_boom()
+{
+       if(self.realowner.vehicle)
+               return;
+
+       vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins_z - 24);
+       tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner);
+       locout = trace_endpos;
+
+       makevectors(self.realowner.angles);
+
+       entity oldself = self;
+       self = self.realowner;
+       MUTATOR_CALLHOOK(PortalTeleport);
+       self.realowner = self;
+       self = oldself;
+
+       TeleportPlayer(self, self.realowner, locout, self.realowner.mangle, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+}
+
+void nade_spawn_boom()
+{
+       entity spawnloc = spawn();
+       setorigin(spawnloc, self.origin);
+       setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
+       spawnloc.movetype = MOVETYPE_NONE;
+       spawnloc.solid = SOLID_NOT;
+       spawnloc.drawonlytoclient = self.realowner;
+       spawnloc.effects = EF_STARDUST;
+       spawnloc.cnt = autocvar_g_nades_spawn_count;
+
+       if(self.realowner.nade_spawnloc)
+       {
+               remove(self.realowner.nade_spawnloc);
+               self.realowner.nade_spawnloc = world;
+       }
+
+       self.realowner.nade_spawnloc = spawnloc;
+}
+
+void nade_heal_think()
+{
+       if(time >= self.ltime)
+       {
+               remove(self);
+               return;
+       }
+       
+       self.nextthink = time;
+       
+       if(time >= self.nade_special_time)
+       {
+               self.nade_special_time = time+0.25;
+               self.nade_show_particles = 1;
+       }
+       else
+               self.nade_show_particles = 0;
+}
+
+void nade_heal_touch()
+{
+       float maxhealth;
+       float health_factor;
+       if(IS_PLAYER(other) || (other.flags & FL_MONSTER))
+       if(other.deadflag == DEAD_NO)
+       if(!other.frozen)
+       {
+               health_factor = autocvar_g_nades_heal_rate*frametime/2;
+               if ( other != self.realowner )
+               {
+                       if ( SAME_TEAM(other,self) )
+                               health_factor *= autocvar_g_nades_heal_friend;
+                       else
+                               health_factor *= autocvar_g_nades_heal_foe;
+               }
+               if ( health_factor > 0 )
+               {
+                       maxhealth = (other.flags & FL_MONSTER) ? other.max_health : g_pickup_healthmega_max;
+                       if ( other.health < maxhealth )
+                       {
+                               if ( self.nade_show_particles )
+                                       pointparticles(particleeffectnum("healing_fx"), other.origin, '0 0 0', 1);
+                               other.health = min(other.health+health_factor, maxhealth);
+                       }
+                       other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);  
+               }
+               else if ( health_factor < 0 )
+               {
+                       Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL,other.origin,'0 0 0');
+               }
+               
+       }
+       
+       if ( IS_REAL_CLIENT(other) || (other.vehicle_flags & VHF_ISVEHICLE) )
+       {
+               entity show_red = (other.vehicle_flags & VHF_ISVEHICLE) ? other.owner : other;
+               show_red.stat_healing_orb = time+0.1;
+               show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
+       }
+}
+
+void nade_heal_boom()
+{
+       entity healer;
+       healer = spawn();
+       healer.owner = self.owner;
+       healer.realowner = self.realowner;
+       setorigin(healer, self.origin);
+       healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
+       healer.ltime = time + healer.healer_lifetime;
+       healer.team = self.realowner.team;
+       healer.bot_dodge = FALSE;
+       healer.solid = SOLID_TRIGGER;
+       healer.touch = nade_heal_touch;
+
+       setmodel(healer, "models/ctf/shield.md3");
+       healer.healer_radius = autocvar_g_nades_nade_radius;
+       vector size = '1 1 1' * healer.healer_radius / 2;
+       setsize(healer,-size,size);
+       
+       Net_LinkEntity(healer, TRUE, 0, healer_send);
+       
+       healer.think = nade_heal_think;
+       healer.nextthink = time;
+       healer.SendFlags |= 1;
+}
+
+void nade_monster_boom()
+{
+       entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, FALSE, 1);
+       
+       if(autocvar_g_nades_pokenade_monster_lifetime > 0)
+               e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
+       e.monster_skill = MONSTER_SKILL_INSANE;
 }
 
 void nade_boom()
 {
        string expef;
+       float nade_blast = 1;
 
-       switch(self.realowner.team)
+       switch ( self.nade_type )
        {
-               case NUM_TEAM_1: expef = "nade_red_explode"; break;
-               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
-               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
-               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
-               default:                 expef = "nade_explode"; break;
+               case NADE_TYPE_NAPALM:
+                       nade_blast = autocvar_g_nades_napalm_blast;
+                       expef = "explosion_medium";
+                       break;
+               case NADE_TYPE_ICE:
+                       nade_blast = 0;
+                       expef = "electro_combo"; // hookbomb_explode electro_combo bigplasma_impact
+                       break;
+               case NADE_TYPE_TRANSLOCATE:
+                       nade_blast = 0;
+                       expef = "";
+                       break;
+               case NADE_TYPE_MONSTER:
+               case NADE_TYPE_SPAWN:
+                       nade_blast = 0;
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "spawn_event_red"; break;
+                               case NUM_TEAM_2: expef = "spawn_event_blue"; break;
+                               case NUM_TEAM_3: expef = "spawn_event_yellow"; break;
+                               case NUM_TEAM_4: expef = "spawn_event_pink"; break;
+                               default: expef = "spawn_event_neutral"; break;
+                       }
+                       break;
+               case NADE_TYPE_HEAL:
+                       nade_blast = 0;
+                       expef = "spawn_event_red";
+                       break;
+
+               default:
+               case NADE_TYPE_NORMAL:
+                       switch(self.realowner.team)
+                       {
+                               case NUM_TEAM_1: expef = "nade_red_explode"; break;
+                               case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+                               case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+                               case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+                               default:                 expef = "nade_neutral_explode"; break;
+                       }
        }
 
-       sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
-       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
        pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
 
-       Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+       sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
+       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 
        self.takedamage = DAMAGE_NO;
-       RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+
+       if(nade_blast)
+       {
+               RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
                                 autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+               Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+       }
+
+       switch ( self.nade_type )
+       {
+               case NADE_TYPE_NAPALM: nade_napalm_boom(); break;
+               case NADE_TYPE_ICE: nade_ice_boom(); break;
+               case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
+               case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
+               case NADE_TYPE_HEAL: nade_heal_boom(); break;
+               case NADE_TYPE_MONSTER: nade_monster_boom(); break;
+       }
 
        remove(self);
 }
 
 void nade_touch()
 {
+       if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; }
        PROJECTILE_TOUCH;
        //setsize(self, '-2 -2 -2', '2 2 2');
        //UpdateCSQCProjectile(self);
@@ -101,6 +572,9 @@ void nade_beep()
 
 void nade_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
+       if(self.nade_type == NADE_TYPE_TRANSLOCATE || self.nade_type == NADE_TYPE_SPAWN)
+               return;
+
        if(DEATH_ISWEAPON(deathtype, WEP_LASER))
                return;
 
@@ -113,14 +587,14 @@ void nade_damage(entity inflictor, entity attacker, float damage, float deathtyp
        if(DEATH_ISWEAPON(deathtype, WEP_UZI))
                damage = self.max_health * 0.1;
 
-       if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) && !(deathtype & HITTYPE_SECONDARY))
-               damage = self.max_health * 1.1;
-
-       if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) && (deathtype & HITTYPE_SECONDARY))
+       if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN))
+       if(deathtype & HITTYPE_SECONDARY)
        {
                damage = self.max_health * 0.1;
-               force *= 15;
+               force *= 10;
        }
+       else
+               damage = self.max_health * 1.1;
 
        self.velocity += force;
 
@@ -134,8 +608,10 @@ void nade_damage(entity inflictor, entity attacker, float damage, float deathtyp
                self.think = nade_beep;
        }
 
-       self.health   -= damage;
-       self.realowner = attacker;
+       self.health       -= damage;
+       
+       if ( self.nade_type != NADE_TYPE_HEAL || IS_PLAYER(attacker) )
+               self.realowner = attacker;
 
        if(self.health <= 0)
                W_PrepareExplosionByDamage(attacker, nade_boom);
@@ -145,6 +621,9 @@ void nade_damage(entity inflictor, entity attacker, float damage, float deathtyp
 
 void toss_nade(entity e, vector _velocity, float _time)
 {
+       if(e.nade == world)
+               return;
+
        entity _nade = e.nade;
        e.nade = world;
 
@@ -157,10 +636,9 @@ void toss_nade(entity e, vector _velocity, float _time)
 
        Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
 
-       //setorigin(_nade, CENTER_OR_VIEWOFS(e) + (v_right * 10) * -1);
        setorigin(_nade, w_shotorg + (v_right * 25) * -1);
-       setmodel(_nade, "models/weapons/v_ok_grenade.md3");
-       setattachment(_nade, world, "");
+       //setmodel(_nade, "models/weapons/v_ok_grenade.md3");
+       //setattachment(_nade, world, "");
        PROJECTILE_MAKETRIGGER(_nade);
        setsize(_nade, '-16 -16 -16', '16 16 16');
        _nade.movetype = MOVETYPE_BOUNCE;
@@ -177,12 +655,16 @@ void toss_nade(entity e, vector _velocity, float _time)
                _nade.velocity = _velocity;
        else
                _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, TRUE);
-               
+
        _nade.touch = nade_touch;
        _nade.health = autocvar_g_nades_nade_health;
        _nade.max_health = _nade.health;
        _nade.takedamage = DAMAGE_AIM;
        _nade.event_damage = nade_damage;
+       _nade.customizeentityforclient = func_null;
+       _nade.exteriormodeltoclient = world;
+       _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       _nade.traileffectnum = 0;
        _nade.teleportable = TRUE;
        _nade.pushable = TRUE;
        _nade.gravity = 1;
@@ -190,6 +672,9 @@ void toss_nade(entity e, vector _velocity, float _time)
        _nade.damagedbycontents = TRUE;
        _nade.angles = vectoangles(_nade.velocity);
        _nade.flags = FL_PROJECTILE;
+       _nade.projectiledeathtype = DEATH_NADE;
+       _nade.toss_time = time;
+       _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
 
        nade_spawn(_nade);
 
@@ -200,6 +685,56 @@ void toss_nade(entity e, vector _velocity, float _time)
        }
 
        e.nade_refire = time + autocvar_g_nades_nade_refire;
+       e.nade_timer = 0;
+}
+
+void nades_GiveBonus(entity player, float score)
+{
+       if (autocvar_g_nades)
+       if (autocvar_g_nades_bonus)
+       if (IS_REAL_CLIENT(player))
+       if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max)
+       if (player.frozen == 0)
+       if (player.deadflag == DEAD_NO)
+       {
+               if ( player.bonus_nade_score < 1 )
+                       player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max;
+
+               if ( player.bonus_nade_score >= 1 )
+               {
+                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
+                       play2(player,"kh/alarm.wav");
+                       player.bonus_nades++;
+                       player.bonus_nade_score -= 1;
+               }
+       }
+}
+
+void nades_RemoveBonus(entity player)
+{
+       player.bonus_nades = player.bonus_nade_score = 0;
+}
+
+float nade_customize()
+{
+       //if(IS_SPEC(other)) { return FALSE; }
+       if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner))
+       {
+               // somewhat hide the model, but keep the glow
+               //self.effects = 0;
+               if(self.traileffectnum)
+                       self.traileffectnum = 0;
+               self.alpha = -1;
+       }
+       else
+       {
+               //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+               if(!self.traileffectnum)
+                       self.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(self.nade_type, FALSE), self.team));
+               self.alpha = 1;
+       }
+       
+       return TRUE;
 }
 
 void nade_prime()
@@ -210,29 +745,53 @@ void nade_prime()
        if(self.fake_nade)
                remove(self.fake_nade);
 
-       self.nade = spawn();
-       setmodel(self.nade, "null");
-       setattachment(self.nade, self, "bip01 l hand");
-       self.nade.classname = "nade";
-       self.nade.realowner = self;
-       self.nade.colormap = self.colormap;
-       self.nade.glowmod = self.glowmod;
-       self.nade.wait = time + autocvar_g_nades_nade_lifetime;
-       self.nade.lifetime = time;
-       self.nade.think = nade_beep;
-       self.nade.nextthink = max(self.nade.wait - 3, time);
-       self.nade.projectiledeathtype = DEATH_NADE;
-
-       self.fake_nade = spawn();
-       setmodel(self.fake_nade, "models/weapons/h_ok_grenade.iqm");
-       setattachment(self.fake_nade, self.weaponentity, "");
-       self.fake_nade.classname = "fake_nade";
-       //self.fake_nade.viewmodelforclient = self;
-       self.fake_nade.realowner = self.fake_nade.owner = self;
-       self.fake_nade.colormap = self.colormap;
-       self.fake_nade.glowmod = self.glowmod;
-       self.fake_nade.think = SUB_Remove;
-       self.fake_nade.nextthink = self.nade.wait;
+       entity n = spawn(), fn = spawn();
+
+       n.classname = "nade";
+       fn.classname = "fake_nade";
+
+       if(self.items & IT_STRENGTH && autocvar_g_nades_bonus_onstrength)
+               n.nade_type = self.nade_type;
+       else if (self.bonus_nades >= 1)
+       {
+               n.nade_type = self.nade_type;
+               n.pokenade_type = self.pokenade_type;
+               self.bonus_nades -= 1;
+       }
+       else
+       {
+               n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type);
+               n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type);
+       }
+       
+       n.nade_type = bound(1, n.nade_type, NADE_TYPE_LAST);
+
+       setmodel(n, "models/weapons/v_ok_grenade.md3");
+       //setattachment(n, self, "bip01 l hand");
+       n.exteriormodeltoclient = self;
+       n.customizeentityforclient = nade_customize;
+       n.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(n.nade_type, FALSE), self.team));
+       n.colormod = Nade_Color(n.nade_type);
+       n.realowner = self;
+       n.colormap = self.colormap;
+       n.glowmod = self.glowmod;
+       n.wait = time + autocvar_g_nades_nade_lifetime;
+       n.lifetime = time;
+       n.think = nade_beep;
+       n.nextthink = max(n.wait - 3, time);
+       n.projectiledeathtype = DEATH_NADE;
+
+       setmodel(fn, "models/weapons/h_ok_grenade.iqm");
+       setattachment(fn, self.weaponentity, "");
+       fn.realowner = fn.owner = self;
+       fn.colormod = Nade_Color(n.nade_type);
+       fn.colormap = self.colormap;
+       fn.glowmod = self.glowmod;
+       fn.think = SUB_Remove;
+       fn.nextthink = n.wait;
+
+       self.nade = n;
+       self.fake_nade = fn;
 }
 
 float CanThrowNade()
@@ -285,21 +844,55 @@ void nades_CheckThrow()
        }
 }
 
+void nades_Clear(entity player)
+{
+       if(player.nade)
+               remove(player.nade);
+       if(player.fake_nade)
+               remove(player.fake_nade);
+
+       player.nade = player.fake_nade = world;
+       player.nade_timer = 0;
+}
+
+MUTATOR_HOOKFUNCTION(nades_CheckThrow)
+{
+       if(MUTATOR_RETURNVALUE) { nades_CheckThrow(); }
+       return FALSE;
+}
+
 MUTATOR_HOOKFUNCTION(nades_VehicleEnter)
 {
-       if(other.nade)
-               toss_nade(other, '0 0 100', max(other.nade.wait, time + 0.05));
+       if(vh_player.nade)
+               toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05));
 
        return FALSE;
 }
 
 MUTATOR_HOOKFUNCTION(nades_PlayerPreThink)
 {
-       float key_pressed = ((g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK)) ? self.button16 : self.BUTTON_HOOK);
+       if(!IS_PLAYER(self)) { return FALSE; }
+
+       float key_pressed = self.BUTTON_HOOK;
+       float time_score;
 
+       if(g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK) || g_jetpack || self.items & IT_JETPACK)
+               key_pressed = self.button16; // if hook/jetpack is enabled, use an alternate key
+               
        if(self.nade)
-               if(self.nade.wait - 0.1 <= time)
-                       toss_nade(self, '0 0 0', time + 0.05);
+       {
+               self.nade_timer = bound(0, (time - self.nade.lifetime) / autocvar_g_nades_nade_lifetime, 1);
+               //print(sprintf("%d %d\n", self.nade_timer, time - self.nade.lifetime));
+               makevectors(self.angles);
+               self.nade.velocity = self.velocity;
+
+               setorigin(self.nade, self.origin + self.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
+               self.nade.angles_y = self.angles_y;
+       }
+
+       if(self.nade)
+       if(self.nade.wait - 0.1 <= time)
+               toss_nade(self, '0 0 0', time + 0.05);
 
        if(CanThrowNade())
        if(self.nade_refire < time)
@@ -322,6 +915,88 @@ MUTATOR_HOOKFUNCTION(nades_PlayerPreThink)
                }
        }
 
+       if(IS_PLAYER(self))
+       {
+               if ( autocvar_g_nades_bonus && autocvar_g_nades )
+               {
+                       entity key;
+                       float key_count = 0;
+                       FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
+
+                       if(self.flagcarried || self.ballcarried) // this player is important
+                               time_score = autocvar_g_nades_bonus_score_time_flagcarrier;
+                       else
+                               time_score = autocvar_g_nades_bonus_score_time;
+                               
+                       if(key_count)
+                               time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding
+
+                       if(autocvar_g_nades_bonus_client_select)
+                       {
+                               self.nade_type = self.cvar_cl_nade_type;
+                               self.pokenade_type = self.cvar_cl_pokenade_type;
+                       }
+                       else
+                       {
+                               self.nade_type = autocvar_g_nades_bonus_type;
+                               self.pokenade_type = autocvar_g_nades_pokenade_monster_type;
+                       }
+                               
+                       self.nade_type = bound(1, self.nade_type, NADE_TYPE_LAST);
+
+                       if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max)
+                               nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max);
+               }
+               else
+               {
+                       self.bonus_nades = self.bonus_nade_score = 0;
+               }
+       }
+
+       float n = 0;
+       entity o = world;
+       if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+               n = -1;
+       else
+       {
+               vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+               n = 0;
+               FOR_EACH_PLAYER(other) if(self != other)
+               {
+                       if(other.deadflag == DEAD_NO)
+                       if(other.frozen == 0)
+                       if(SAME_TEAM(other, self))
+                       if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+                       {
+                               if(!o)
+                                       o = other;
+                               if(self.frozen == 1)
+                                       other.reviving = TRUE;
+                               ++n;
+                       }
+               }
+       }
+
+       if(n && self.frozen == 3) // OK, there is at least one teammate reviving us
+       {
+               self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+               self.health = max(1, self.revive_progress * start_health);
+
+               if(self.revive_progress >= 1)
+               {
+                       Unfreeze(self);
+
+                       Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
+                       Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
+               }
+
+               FOR_EACH_PLAYER(other) if(other.reviving)
+               {
+                       other.revive_progress = self.revive_progress;
+                       other.reviving = FALSE;
+               }
+       }
+
        return FALSE;
 }
 
@@ -332,24 +1007,113 @@ MUTATOR_HOOKFUNCTION(nades_PlayerSpawn)
        else
                self.nade_refire  = time + autocvar_g_nades_nade_refire;
 
+       if(autocvar_g_nades_bonus_client_select)
+               self.nade_type = self.cvar_cl_nade_type;
+
+       self.nade_timer = 0;
+
+       if(self.nade_spawnloc)
+       {
+               setorigin(self, self.nade_spawnloc.origin);
+               self.nade_spawnloc.cnt -= 1;
+               
+               if(self.nade_spawnloc.cnt <= 0)
+               {
+                       remove(self.nade_spawnloc);
+                       self.nade_spawnloc = world;
+               }
+       }
+
        return FALSE;
 }
 
 MUTATOR_HOOKFUNCTION(nades_PlayerDies)
 {
-       if(self.nade)
-               toss_nade(self, '0 0 100', max(self.nade.wait, time + 0.05));
+       if(frag_target.nade)
+       if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
+               toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
+
+       float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
+
+       if(IS_PLAYER(frag_attacker))
+       {
+               if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target)
+                       nades_RemoveBonus(frag_attacker);
+               else if(frag_target.flagcarried)
+                       nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium);
+               else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1)
+               {
+                       #define SPREE_ITEM(counta,countb,center,normal,gentle) \
+                               case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; }
+                       switch(frag_attacker.killcount)
+                       {
+                               KILL_SPREE_LIST
+                               default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break;
+                       }
+                       #undef SPREE_ITEM
+               }
+               else
+                       nades_GiveBonus(frag_attacker, killcount_bonus);
+       }
+
+       nades_RemoveBonus(frag_target);
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_PlayerDamage)
+{
+       if(frag_target.frozen)
+       if(autocvar_g_freezetag_revive_nade)
+       if(frag_attacker == frag_target)
+       if(frag_deathtype == DEATH_NADE)
+       if(time - frag_inflictor.toss_time <= 0.1)
+       {
+               Unfreeze(frag_target);
+               frag_target.health = autocvar_g_freezetag_revive_nade_health;
+               pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
+               frag_damage = 0;
+               frag_force = '0 0 0';
+               Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
+               Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+       }
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_MonsterDies)
+{
+       if(IS_PLAYER(frag_attacker))
+       if(DIFF_TEAM(frag_attacker, self))
+       if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
+               nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor);
 
        return FALSE;
 }
 
 MUTATOR_HOOKFUNCTION(nades_RemovePlayer)
 {
-       if(self.nade)
-               remove(self.nade);
+       nades_Clear(self);
+       nades_RemoveBonus(self);
+       return FALSE;
+}
 
-       if(self.fake_nade)
-               remove(self.fake_nade);
+MUTATOR_HOOKFUNCTION(nades_SpectateCopy)
+{
+       self.nade_timer = other.nade_timer;
+       self.nade_type = other.nade_type;
+       self.pokenade_type = other.pokenade_type;
+       self.bonus_nades = other.bonus_nades;
+       self.bonus_nade_score = other.bonus_nade_score;
+       self.stat_healing_orb = other.stat_healing_orb;
+       self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_GetCvars)
+{
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type");
+       GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type");
 
        return FALSE;
 }
@@ -366,31 +1130,50 @@ MUTATOR_HOOKFUNCTION(nades_BuildMutatorsPrettyString)
        return FALSE;
 }
 
+void nades_Initialize()
+{
+       addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer);
+       addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades);
+       addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type);
+       addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score);
+       addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb);
+       addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha);
+       
+       precache_model("models/ok_nade_counter/ok_nade_counter.md3");
+       precache_model("models/weapons/h_ok_grenade.iqm");
+       precache_model("models/weapons/v_ok_grenade.md3");
+       precache_model("models/ctf/shield.md3");
+
+       precache_sound("weapons/rocket_impact.wav");
+       precache_sound("weapons/grenade_bounce1.wav");
+       precache_sound("weapons/grenade_bounce2.wav");
+       precache_sound("weapons/grenade_bounce3.wav");
+       precache_sound("weapons/grenade_bounce4.wav");
+       precache_sound("weapons/grenade_bounce5.wav");
+       precache_sound("weapons/grenade_bounce6.wav");
+       precache_sound("overkill/grenadebip.ogg");
+}
+
 MUTATOR_DEFINITION(mutator_nades)
 {
+       MUTATOR_HOOK(ForbidThrowCurrentWeapon, nades_CheckThrow, CBC_ORDER_LAST);
        MUTATOR_HOOK(VehicleEnter, nades_VehicleEnter, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPreThink, nades_PlayerPreThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, nades_PlayerSpawn, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_LAST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, nades_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MonsterDies, nades_MonsterDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(MakePlayerObserver, nades_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, nades_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SpectateCopy, nades_SpectateCopy, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GetCvars, nades_GetCvars, CBC_ORDER_ANY);
+       MUTATOR_HOOK(reset_map_global, nades_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, nades_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsPrettyString, nades_BuildMutatorsPrettyString, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
-               precache_model("models/ok_nade_counter/ok_nade_counter.md3");
-
-               precache_model("models/weapons/h_ok_grenade.iqm");
-               precache_model("models/weapons/v_ok_grenade.md3");
-               precache_sound("weapons/rocket_impact.wav");
-               precache_sound("weapons/grenade_bounce1.wav");
-               precache_sound("weapons/grenade_bounce2.wav");
-               precache_sound("weapons/grenade_bounce3.wav");
-               precache_sound("weapons/grenade_bounce4.wav");
-               precache_sound("weapons/grenade_bounce5.wav");
-               precache_sound("weapons/grenade_bounce6.wav");
-               precache_sound("overkill/grenadebip.ogg");
+               nades_Initialize();
        }
 
        return FALSE;
index 1940f4e0520c0c051bda605b4f5bed3173bf85bc..c6c30c6d53210dfee9143e12646831e7b727f147 100644 (file)
@@ -1,5 +1,26 @@
 .entity nade;
 .entity fake_nade;
+.float nade_timer;
 .float nade_refire;
+.float bonus_nades;
+.float nade_special_time;
+.float bonus_nade_score;
+.float nade_type;
+.string pokenade_type;
+.entity nade_damage_target;
+.float cvar_cl_nade_type;
+.string cvar_cl_pokenade_type;
+.float toss_time;
+.float stat_healing_orb;
+.float stat_healing_orb_alpha;
+.float nade_show_particles;
 
-void() nades_CheckThrow;
+void toss_nade(entity e, vector _velocity, float _time);
+
+// Remove nades that are being thrown
+void(entity player) nades_Clear;
+
+// Give a bonus grenade to a player
+void(entity player, float score) nades_GiveBonus;
+// Remove all bonus nades from a player
+void(entity player) nades_RemoveBonus;
index e53c80f848cca975344b6e2025076bdecfec6c33..ffae9543b95c663527fe29a6abe49f527453c94f 100644 (file)
@@ -57,7 +57,7 @@ MUTATOR_HOOKFUNCTION(msnt_PlayerSpawn)
                        if(team_mate.msnt_timer < time)
                        if(SAME_TEAM(self, team_mate))
                        if(time > team_mate.spawnshieldtime) // spawn shielding
-                       if(team_mate.freezetag_frozen == 0)
+                       if(team_mate.frozen == 0)
                        if(team_mate != self)
                        {
                                tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
index fabf13639cf89d6ccf4ba810a099d303a74d0ff7..30b01b0daa0933351e754df39d45503c17ca2a66 100644 (file)
@@ -19,13 +19,15 @@ void PlayerTouchExplode(entity p1, entity p2)
 MUTATOR_HOOKFUNCTION(touchexplode_PlayerThink)
 {
        if(time > self.touchexplode_time)
-       if (!gameover)
+       if(!gameover)
+       if(!self.frozen)
        if(IS_PLAYER(self))
        if(self.deadflag == DEAD_NO)
        if (!IS_INDEPENDENT_PLAYER(self))
        FOR_EACH_PLAYER(other) if(self != other)
        {
                if(time > other.touchexplode_time)
+               if(!other.frozen)
                if(other.deadflag == DEAD_NO)
                if (!IS_INDEPENDENT_PLAYER(other))
                if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
index c71889ce1412f68dce5ab08a3ea2359c2db30113..288b2477597c00114d0a3b0246c296f3e1360b51 100644 (file)
@@ -16,6 +16,7 @@ sys-post.qh
 ../common/stats.qh
 ../common/teams.qh
 ../common/util.qh
+../common/nades.qh
 ../common/buffs.qh
 ../common/test.qh
 ../common/counting.qh
@@ -210,6 +211,7 @@ target_music.qc
 
 ../common/items.qc
 
+../common/nades.qc
 ../common/buffs.qc
 
 
index 06b484e18c3e33c94499773a4fa8fd86eccc0cb6..607629e42578834ed10e98c99ac66b28c82a8148 100644 (file)
@@ -2,4 +2,4 @@
 float spawnpoint_nag;
 float SpawnEvent_Send(entity to, float sf);
 entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck);
-
+entity SelectSpawnPoint (float anypoint);
index c8a75032bffa6fbca95ea045ebd089baf1c8dc93..b25528e1645ccf1f635a20e5ae2866dc05eaf045 100644 (file)
@@ -155,7 +155,6 @@ float game_delay;
 float game_delay_last;
 
 float RedirectionThink();
-entity SelectSpawnPoint (float anypoint);
 void StartFrame (void)
 {
        execute_next_frame();
index 87ea68d7982ca4714084e0ae8b5a0ea6a5b8dcf6..33d3ebd3603a030a6eb6b934e2708478e544ebef 100644 (file)
@@ -315,6 +315,9 @@ string getwelcomemessage(void)
        if (g_grappling_hook)
                s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
 
+       if (cvar("g_nades"))
+               s = strcat(s, "\n\n^3nades^8 are enabled, press 'g' to use them\n");
+
        if(cache_lastmutatormsg != autocvar_g_mutatormsg)
        {
                if(cache_lastmutatormsg)
index 0ad23a137ec53177cf29fb60390b8ba28f6512ee..ea2cf8bf0560dfee7a879789377231dc82e8cd0f 100644 (file)
@@ -263,7 +263,7 @@ void lgbeam_think()
                return;
        }
 
-       if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.freezetag_frozen)
+       if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.frozen)
        {
                if(self == owner_player.lgbeam)
                        owner_player.lgbeam = world;
index ec36f5d32541cea2fbca1c8fa29572b0e1994d06..e91fb5fb6b77b70889548d314c7065893ddddb03 100644 (file)
@@ -195,7 +195,7 @@ void W_Mine_Think (void)
 
        // a player's mines shall explode if he disconnects or dies
        // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
-       if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.freezetag_frozen)
+       if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
        {
                other = world;
                self.projectiledeathtype |= HITTYPE_BOUNCE;
@@ -207,7 +207,7 @@ void W_Mine_Think (void)
        head = findradius(self.origin, autocvar_g_balance_minelayer_proximityradius);
        while(head)
        {
-               if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.freezetag_frozen)
+               if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
                if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
                if(!self.mine_time)
                {