.float teamkill_soundtime;
.entity teamkill_soundsource;
.entity pusher;
+.float istypefrag;
.float taunt_soundtime;
// after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon
float culprit;
culprit = DEATH_WEAPONOF(deathtype);
- if(!culprit || !WEPSET_CONTAINS_EW(attacker, culprit))
+ if(!culprit)
+ culprit = attacker.weapon;
+ else if(!WEPSET_CONTAINS_EW(attacker, culprit))
culprit = attacker.weapon;
if(g_weaponarena_random_with_laser && culprit == WEP_LASER)
}
f = 0;
}
- else if(g_ctf)
- {
- if(g_ctf_ignore_frags)
- f = 0;
- }
}
attacker.totalfrags += f;
string Obituary_ExtraFragInfo(entity player) // Extra fragmessage information
{
- string health_output;
- string ping_output;
- string handicap_output;
- string output;
+ string health_output = string_null;
+ string ping_output = string_null;
+ string handicap_output = string_null;
+ string output = string_null;
if(autocvar_sv_fraginfo && ((autocvar_sv_fraginfo == 2) || inWarmupStage))
{
void Send_KillNotification (string s1, string s2, string s3, float msg, float type)
{
- WriteByte(MSG_ALL, SVC_TEMPENTITY);
- WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
- WriteString(MSG_ALL, s1);
- WriteString(MSG_ALL, s2);
- WriteString(MSG_ALL, s3);
- WriteShort(MSG_ALL, msg);
- WriteByte(MSG_ALL, type);
+ WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
+ WriteByte(MSG_BROADCAST, TE_CSQC_KILLNOTIFY);
+ WriteString(MSG_BROADCAST, s1);
+ WriteString(MSG_BROADCAST, s2);
+ WriteString(MSG_BROADCAST, s3);
+ WriteShort(MSG_BROADCAST, msg);
+ WriteByte(MSG_BROADCAST, type);
}
// Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
{
string s, a, msg;
- float w, type;
+ float type;
if (targ.classname == "player")
{
{
if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
msg = ColoredTeamName(targ.team); // TODO: check if needed?
+ else
+ msg = "";
if(!g_cts) // no "killed your own dumb self" message in CTS
Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE);
if (targ.killcount > 2)
msg = ftos(targ.killcount);
+ else
+ msg = "";
if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
{
if(attacker.team == COLOR_TEAM1)
deathtype = KILL_TEAM_BLUE;
}
- Send_KillNotification(s, msg, ftos(w), deathtype, MSG_SUICIDE);
+ Send_KillNotification(s, msg, "", deathtype, MSG_SUICIDE);
}
else if (attacker.classname == "player")
{
Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL);
- if (targ.killcount > 2) {
+ if (targ.killcount > 2)
msg = ftos(targ.killcount);
- }
+ else
+ msg = "";
if (attacker.killcount > 2) {
msg = ftos(attacker.killcount);
PlayerStats_Event(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
}
- if(targ.BUTTON_CHAT) {
+ if(targ.istypefrag) {
Send_CSQC_KillCenterprint(attacker, s, Obituary_ExtraFragInfo(targ), KILL_TYPEFRAG, MSG_KILL);
Send_CSQC_KillCenterprint(targ, a, Obituary_ExtraFragInfo(attacker), KILL_TYPEFRAGGED, MSG_KILL);
} else {
attacker.taunt_soundtime = time + 1;
- // TODO: fix this?
- if (deathtype == DEATH_CUSTOM)
+ if (deathtype == DEATH_HURTTRIGGER && inflictor.message2 != "")
+ msg = inflictor.message2;
+ else if (deathtype == DEATH_CUSTOM)
msg = deathmessage;
else
- msg = inflictor.message2;
+ msg = "";
if(strstrofs(msg, "%", 0) < 0)
msg = strcat("%s ", msg, " by %s");
Send_KillNotification(a, s, msg, deathtype, MSG_KILL);
- if(g_ctf && targ.flagcarried)
- {
- UpdateFrags(attacker, ctf_score_value("score_kill"));
- PlayerScore_Add(attacker, SP_CTF_FCKILLS, 1);
- GiveFrags(attacker, targ, 0, deathtype); // for logging
- }
- else
- GiveFrags(attacker, targ, 1, deathtype);
+ GiveFrags(attacker, targ, 1, deathtype);
if (targ.killcount > 2) {
Send_KillNotification(s, ftos(targ.killcount), a, KILL_END_SPREE, MSG_SPREE);
attacker.killcount = attacker.killcount + 1;
- if (attacker.killcount > 2) {
- Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE);
- }
- else if (attacker.killcount == 3)
+ if (attacker.killcount == 3)
{
Send_KillNotification(a, "", "", KILL_SPREE_3, MSG_SPREE);
AnnounceTo(attacker, "03kills");
AnnounceTo(attacker, "30kills");
PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30, 1);
}
+ else if (attacker.killcount > 2) {
+ Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE);
+ }
LogDeath("frag", deathtype, attacker, targ);
}
}
msg = inflictor.message;
else if (deathtype == DEATH_CUSTOM)
msg = deathmessage;
+ else
+ msg = "";
if(strstrofs(msg, "%", 0) < 0)
msg = strcat("%s ", msg);
}
}
+void Ice_Think()
+{
+ if(self.owner.health < 1)
+ {
+ remove(self);
+ return;
+ }
+ setorigin(self, self.owner.origin - '0 0 16');
+ self.nextthink = time;
+}
+
+void Freeze (entity targ, float freeze_time)
+{
+ float monster = (targ.flags & FL_MONSTER);
+ float player = (targ.flags & FL_CLIENT);
+
+ if(!player && !monster) // only specified entities can be freezed
+ return;
+
+ if(targ.frozen || targ.freezetag_frozen)
+ return;
+
+ targ.frozen = 1;
+ targ.revive_progress = 0;
+ targ.health = 1;
+ targ.revive_speed = freeze_time;
+
+ entity ice;
+ 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");
+
+ entity oldself;
+ oldself = self;
+ self = ice;
+ Ice_Think();
+ self = oldself;
+
+ RemoveGrapplingHook(targ);
+}
+
+void Unfreeze (entity targ)
+{
+ targ.frozen = 0;
+ targ.revive_progress = 0;
+ targ.health = ((targ.classname == STR_PLAYER) ? autocvar_g_balance_health_start : targ.max_health);
+
+ // remove the ice block
+ entity ice;
+ for(ice = world; (ice = find(ice, classname, "ice")); ) if(ice.owner == targ)
+ {
+ remove(ice);
+ break;
+ }
+}
+
// these are updated by each Damage call for use in button triggering and such
entity damage_targ;
entity damage_inflictor;
if(autocvar_g_mirrordamage_virtual)
{
- vector v;
- v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
- v_z = 0; // fteqcc sucks
+ vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
attacker.dmg_take += v_x;
attacker.dmg_save += v_y;
attacker.dmg_inflictor = inflictor;
- mirrordamage = 0;
+ mirrordamage = v_z; // = 0, to make fteqcc stfu
mirrorforce = 0;
}
if(autocvar_g_friendlyfire_virtual)
{
- vector v;
- v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage);
- v_z = 0; // fteqcc sucks
+ vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, damage);
targ.dmg_take += v_x;
targ.dmg_save += v_y;
targ.dmg_inflictor = inflictor;
mirrorforce *= g_weaponforcefactor;
}
+ if(targ.frozen && attacker.classname != "monster_spider")
+ {
+ damage = 0;
+ force *= 0.2;
+ }
+
// should this be changed at all? If so, in what way?
frag_attacker = attacker;
frag_target = targ;
damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
}
- // CTF: reduce damage/force
- if(g_ctf)
- if(targ == attacker)
- if(targ.flagcarried)
- {
- damage = damage * autocvar_g_ctf_flagcarrier_selfdamage;
- force = force * autocvar_g_ctf_flagcarrier_selfforce;
- }
-
if(g_runematch)
{
// apply strength rune
else
victim = targ;
- if(victim.classname == "player" || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+ if(victim.classname == "player" || victim.turrcaps_flags & TFL_TURRCAPS_ISTURRET || victim.flags & FL_MONSTER)
{
if(IsDifferentTeam(victim, attacker))
{
if(deathtype & HITTYPE_HEADSHOT)
headshot = 1;
}
- if(g_ca)
- PlayerScore_Add(attacker, SP_SCORE, damage * autocvar_g_ca_damage2score_multiplier);
}
}
else
myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
// if it's a player, use the view origin as reference
- if (targ.classname == "player")
- center = targ.origin + targ.view_ofs;
- else
- center = targ.origin + (targ.mins + targ.maxs) * 0.5;
+ center = CENTER_OR_VIEWOFS(targ);
force = normalize(center - myblastorigin);
force = force * (finaldmg / coredamage) * forceintensity;
if(maxtime > mintime || maxdps > mindps)
{
+ // Constraints:
+
+ // damage we have right now
mindamage = mindps * mintime;
- maxdamage = mindamage + d;
- // interval [mintime, maxtime] * [mindps, maxdps]
- // intersected with
- // [mindamage, maxdamage]
- // maximum of this!
-
- if(maxdamage >= maxtime * maxdps)
- {
- totaltime = maxtime;
- totaldamage = maxtime * maxdps;
-
- // this branch increases totaldamage if either t > mintime, or dps > mindps
- }
- else
- {
- // maxdamage is inside the interval!
- // first, try to use mindps; only if this fails, increase dps as needed
- totaltime = min(maxdamage / mindps, maxtime); // maxdamage / mindps >= mindamage / mindps = mintime
- totaldamage = maxdamage;
- // can totaldamage / totaltime be >= maxdps?
- // max(mindps, maxdamage / maxtime) >= maxdps?
- // we know maxdamage < maxtime * maxdps
- // so it cannot be
-
- // this branch ALWAYS increases totaldamage, but requires maxdamage < maxtime * maxdps
- }
+ // damage we want to get
+ maxdamage = mindamage + d;
- // total conditions for increasing:
- // maxtime > mintime OR maxdps > mindps OR maxtime * maxdps > maxdamage
- // however:
- // if maxtime = mintime, maxdps = mindps
- // then:
- // maxdamage = mindamage + d
- // mindamage = mindps * mintime = maxdps * maxtime < maxdamage!
- // so the last condition is not needed
+ // but we can't exceed maxtime * maxdps!
+ totaldamage = min(maxdamage, maxtime * maxdps);
+
+ // LEMMA:
+ // Look at:
+ // totaldamage = min(mindamage + d, maxtime * maxdps)
+ // We see:
+ // totaldamage <= maxtime * maxdps
+ // ==> totaldamage / maxdps <= maxtime.
+ // We also see:
+ // totaldamage / mindps = min(mindamage / mindps + d, maxtime * maxdps / mindps)
+ // >= min(mintime, maxtime)
+ // ==> totaldamage / maxdps >= mintime.
+
+ /*
+ // how long do we damage then?
+ // at least as long as before
+ // but, never exceed maxdps
+ totaltime = max(mintime, totaldamage / maxdps); // always <= maxtime due to lemma
+ */
+
+ // alternate:
+ // at most as long as maximum allowed
+ // but, never below mindps
+ totaltime = min(maxtime, totaldamage / mindps); // always >= mintime due to lemma
+
+ // assuming t > mintime, dps > mindps:
+ // we get d = t * dps = maxtime * maxdps
+ // totaldamage = min(maxdamage, maxtime * maxdps) = min(... + d, maxtime * maxdps) = maxtime * maxdps
+ // totaldamage / maxdps = maxtime
+ // totaldamage / mindps > totaldamage / maxdps = maxtime
+ // FROM THIS:
+ // a) totaltime = max(mintime, maxtime) = maxtime
+ // b) totaltime = min(maxtime, totaldamage / maxdps) = maxtime
+
+ // assuming t <= mintime:
+ // we get maxtime = mintime
+ // a) totaltime = max(mintime, ...) >= mintime, also totaltime <= maxtime by the lemma, therefore totaltime = mintime = maxtime
+ // b) totaltime = min(maxtime, ...) <= maxtime, also totaltime >= mintime by the lemma, therefore totaltime = mintime = maxtime
+
+ // assuming dps <= mindps:
+ // we get mindps = maxdps.
+ // With this, the lemma says that mintime <= totaldamage / mindps = totaldamage / maxdps <= maxtime.
+ // a) totaltime = max(mintime, totaldamage / maxdps) = totaldamage / maxdps
+ // b) totaltime = min(maxtime, totaldamage / mindps) = totaldamage / maxdps
e.fire_damagepersec = totaldamage / totaltime;
e.fire_endtime = time + totaltime;
e.fire_endtime = 0;
// ice stops fire
- if(e.freezetag_frozen)
+ if(e.freezetag_frozen || e.frozen)
e.fire_endtime = 0;
t = min(frametime, e.fire_endtime - time);