X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fclient.qc;h=f8d10564591b96aca42b9cb13a2d15595b5986d5;hb=9e50112561ad6f4cefe6d13bd7185f5dfd1ada4b;hp=26696f4c87e94bac39bbe2d7e4590c254e5c0c36;hpb=01c31aae02df623f8f9257a79de06bdbb11eb183;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 26696f4c8..f8d105645 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -232,7 +232,7 @@ void PutObserverInServer(entity this) if (IS_PLAYER(this)) { - if(GetResourceAmount(this, RESOURCE_HEALTH) >= 1) + if(GetResource(this, RES_HEALTH) >= 1) { // despawn effect Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); @@ -274,7 +274,7 @@ void PutObserverInServer(entity this) RemoveGrapplingHooks(this); Portal_ClearAll(this); - Unfreeze(this); + Unfreeze(this, false); SetSpectatee(this, NULL); if (this.alivetime) @@ -311,14 +311,14 @@ void PutObserverInServer(entity this) if(this.damagedbycontents) IL_REMOVE(g_damagedbycontents, this); this.damagedbycontents = false; - SetResourceAmountExplicit(this, RESOURCE_HEALTH, FRAGS_SPECTATOR); + SetResourceExplicit(this, RES_HEALTH, FRAGS_SPECTATOR); SetSpectatee_status(this, etof(this)); this.takedamage = DAMAGE_NO; this.solid = SOLID_NOT; set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink this.flags = FL_CLIENT | FL_NOTARGET; this.effects = 0; - SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_balance_armor_start); // was 666?! + SetResourceExplicit(this, RES_ARMOR, autocvar_g_balance_armor_start); // was 666?! this.pauserotarmor_finished = 0; this.pauserothealth_finished = 0; this.pauseregen_finished = 0; @@ -347,6 +347,7 @@ void PutObserverInServer(entity this) this.crouch = false; STAT(REVIVE_PROGRESS, this) = 0; this.revival_time = 0; + this.draggable = drag_undraggable; this.items = 0; STAT(WEAPONS, this) = '0 0 0'; @@ -380,7 +381,7 @@ void PutObserverInServer(entity this) if(axh.owner == this && axh != NULL && !wasfreed(axh)) delete(axh); } - + if (mutator_returnvalue) { // mutator prevents resetting teams+score @@ -389,7 +390,9 @@ void PutObserverInServer(entity this) { SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR); this.frags = FRAGS_SPECTATOR; - } + } + if (CS(this).just_joined) + CS(this).just_joined = false; } int player_getspecies(entity this) @@ -554,24 +557,24 @@ void PutPlayerInServer(entity this) this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; if (warmup_stage) { - SetResourceAmount(this, RESOURCE_SHELLS, warmup_start_ammo_shells); - SetResourceAmount(this, RESOURCE_BULLETS, warmup_start_ammo_nails); - SetResourceAmount(this, RESOURCE_ROCKETS, warmup_start_ammo_rockets); - SetResourceAmount(this, RESOURCE_CELLS, warmup_start_ammo_cells); - SetResourceAmount(this, RESOURCE_PLASMA, warmup_start_ammo_plasma); - SetResourceAmount(this, RESOURCE_FUEL, warmup_start_ammo_fuel); - SetResourceAmount(this, RESOURCE_HEALTH, warmup_start_health); - SetResourceAmount(this, RESOURCE_ARMOR, warmup_start_armorvalue); + SetResource(this, RES_SHELLS, warmup_start_ammo_shells); + SetResource(this, RES_BULLETS, warmup_start_ammo_nails); + SetResource(this, RES_ROCKETS, warmup_start_ammo_rockets); + SetResource(this, RES_CELLS, warmup_start_ammo_cells); + SetResource(this, RES_PLASMA, warmup_start_ammo_plasma); + SetResource(this, RES_FUEL, warmup_start_ammo_fuel); + SetResource(this, RES_HEALTH, warmup_start_health); + SetResource(this, RES_ARMOR, warmup_start_armorvalue); STAT(WEAPONS, this) = WARMUP_START_WEAPONS; } else { - SetResourceAmount(this, RESOURCE_SHELLS, start_ammo_shells); - SetResourceAmount(this, RESOURCE_BULLETS, start_ammo_nails); - SetResourceAmount(this, RESOURCE_ROCKETS, start_ammo_rockets); - SetResourceAmount(this, RESOURCE_CELLS, start_ammo_cells); - SetResourceAmount(this, RESOURCE_PLASMA, start_ammo_plasma); - SetResourceAmount(this, RESOURCE_FUEL, start_ammo_fuel); - SetResourceAmount(this, RESOURCE_HEALTH, start_health); - SetResourceAmount(this, RESOURCE_ARMOR, start_armorvalue); + SetResource(this, RES_SHELLS, start_ammo_shells); + SetResource(this, RES_BULLETS, start_ammo_nails); + SetResource(this, RES_ROCKETS, start_ammo_rockets); + SetResource(this, RES_CELLS, start_ammo_cells); + SetResource(this, RES_PLASMA, start_ammo_plasma); + SetResource(this, RES_FUEL, start_ammo_fuel); + SetResource(this, RES_HEALTH, start_health); + SetResource(this, RES_ARMOR, start_armorvalue); STAT(WEAPONS, this) = start_weapons; if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false) { @@ -621,7 +624,10 @@ void PutPlayerInServer(entity this) this.angles = spot.angles; this.angles_z = 0; // never spawn tilted even if the spot says to if (IS_BOT_CLIENT(this)) + { this.v_angle = this.angles; + bot_aim_reset(this); + } this.fixangle = true; // turn this way immediately this.oldvelocity = this.velocity = '0 0 0'; this.avelocity = '0 0 0'; @@ -677,6 +683,8 @@ void PutPlayerInServer(entity this) this.event_damage = PlayerDamage; this.event_heal = PlayerHeal; + this.draggable = func_null; + if(!this.bot_attack) IL_PUSH(g_bot_targets, this); this.bot_attack = true; @@ -733,7 +741,7 @@ void PutPlayerInServer(entity this) //spot.target = s; } - Unfreeze(this); + Unfreeze(this, false); MUTATOR_CALLHOOK(PlayerSpawn, this, spot); @@ -1051,6 +1059,8 @@ string getwelcomemessage(entity this) return s; } +bool autocvar_sv_qcphysics = false; // TODO this is for testing - remove when qcphysics work + /** ============= ClientConnect @@ -1143,7 +1153,7 @@ void ClientConnect(entity this) if (IS_REAL_CLIENT(this)) sv_notice_join(this); - this.move_qcphysics = true; + this.move_qcphysics = autocvar_sv_qcphysics; // update physics stats (players can spawn before physics runs) Physics_UpdateStats(this); @@ -1198,7 +1208,7 @@ void ClientDisconnect(entity this) Portal_ClearAll(this); - Unfreeze(this); + Unfreeze(this, false); RemoveGrapplingHooks(this); @@ -1238,7 +1248,7 @@ void ChatBubbleThink(entity this) if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) { - if ( CS(this.owner).active_minigame ) + if ( CS(this.owner).active_minigame && PHYS_INPUT_BUTTON_MINIGAME(this.owner) ) this.mdl = "models/sprites/minigame_busy.iqm"; else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) this.mdl = "models/misc/chatbubble.spr"; @@ -1565,25 +1575,25 @@ void player_regen(entity this) float mina, maxa, limith, limita; maxa = autocvar_g_balance_armor_rotstable; mina = autocvar_g_balance_armor_regenstable; - limith = GetResourceLimit(this, RESOURCE_HEALTH); - limita = GetResourceLimit(this, RESOURCE_ARMOR); + limith = GetResourceLimit(this, RES_HEALTH); + limita = GetResourceLimit(this, RES_ARMOR); regen_health_rotstable = regen_health_rotstable * max_mod; regen_health_stable = regen_health_stable * max_mod; limith = limith * limit_mod; limita = limita * limit_mod; - SetResourceAmount(this, RESOURCE_ARMOR, CalcRotRegen(GetResourceAmount(this, RESOURCE_ARMOR), mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, + SetResource(this, RES_ARMOR, CalcRotRegen(GetResource(this, RES_ARMOR), mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita)); - SetResourceAmount(this, RESOURCE_HEALTH, CalcRotRegen(GetResourceAmount(this, RESOURCE_HEALTH), regen_health_stable, regen_health, regen_health_linear, + SetResource(this, RES_HEALTH, CalcRotRegen(GetResource(this, RES_HEALTH), regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith)); } // if player rotted to death... die! // check this outside above checks, as player may still be able to rot to death - if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) + if(GetResource(this, RES_HEALTH) < 1) { if(this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -1597,9 +1607,9 @@ void player_regen(entity this) maxf = autocvar_g_balance_fuel_rotstable; minf = autocvar_g_balance_fuel_regenstable; - limitf = GetResourceLimit(this, RESOURCE_FUEL); + limitf = GetResourceLimit(this, RES_FUEL); - SetResourceAmount(this, RESOURCE_FUEL, CalcRotRegen(GetResourceAmount(this, RESOURCE_FUEL), minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, + SetResource(this, RES_FUEL, CalcRotRegen(GetResource(this, RES_FUEL), minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf)); } @@ -1647,15 +1657,15 @@ void SpectateCopy(entity this, entity spectatee) MUTATOR_CALLHOOK(SpectateCopy, spectatee, this); PS(this) = PS(spectatee); this.armortype = spectatee.armortype; - SetResourceAmountExplicit(this, RESOURCE_ARMOR, GetResourceAmount(spectatee, RESOURCE_ARMOR)); - SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(spectatee, RESOURCE_CELLS)); - SetResourceAmountExplicit(this, RESOURCE_PLASMA, GetResourceAmount(spectatee, RESOURCE_PLASMA)); - SetResourceAmountExplicit(this, RESOURCE_SHELLS, GetResourceAmount(spectatee, RESOURCE_SHELLS)); - SetResourceAmountExplicit(this, RESOURCE_BULLETS, GetResourceAmount(spectatee, RESOURCE_BULLETS)); - SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(spectatee, RESOURCE_ROCKETS)); - SetResourceAmountExplicit(this, RESOURCE_FUEL, GetResourceAmount(spectatee, RESOURCE_FUEL)); + SetResourceExplicit(this, RES_ARMOR, GetResource(spectatee, RES_ARMOR)); + SetResourceExplicit(this, RES_CELLS, GetResource(spectatee, RES_CELLS)); + SetResourceExplicit(this, RES_PLASMA, GetResource(spectatee, RES_PLASMA)); + SetResourceExplicit(this, RES_SHELLS, GetResource(spectatee, RES_SHELLS)); + SetResourceExplicit(this, RES_BULLETS, GetResource(spectatee, RES_BULLETS)); + SetResourceExplicit(this, RES_ROCKETS, GetResource(spectatee, RES_ROCKETS)); + SetResourceExplicit(this, RES_FUEL, GetResource(spectatee, RES_FUEL)); this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance - SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(spectatee, RESOURCE_HEALTH)); + SetResourceExplicit(this, RES_HEALTH, GetResource(spectatee, RES_HEALTH)); CS(this).impulse = 0; this.items = spectatee.items; STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee); @@ -1913,6 +1923,14 @@ void Join(entity this) this.team_selected = false; } +int GetPlayerLimit() +{ + int player_limit = autocvar_g_maxplayers; + MUTATOR_CALLHOOK(GetPlayerLimit, player_limit); + player_limit = M_ARGV(0, int); + return player_limit; +} + /** * Determines whether the player is allowed to join. This depends on cvar * g_maxplayers, if it isn't used this function always return true, otherwise @@ -1945,11 +1963,13 @@ int nJoinAllowed(entity this, entity ignore) ++currentlyPlaying; }); + int player_limit = GetPlayerLimit(); + float free_slots = 0; - if (!autocvar_g_maxplayers) + if (!player_limit) free_slots = maxclients - totalClients; - else if(currentlyPlaying < autocvar_g_maxplayers) - free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); + else if(currentlyPlaying < player_limit) + free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying); static float join_prevent_msg_time = 0; if(this && ignore && !free_slots && time > join_prevent_msg_time) @@ -2153,7 +2173,7 @@ bool PlayerThink(entity this) } this.items_added = 0; - if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || GetResourceAmount(this, RESOURCE_FUEL) >= 0.01)) + if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || GetResource(this, RES_FUEL) >= 0.01)) this.items_added |= IT_FUEL; this.items |= this.items_added; @@ -2406,21 +2426,21 @@ void PlayerPreThink (entity this) if(IS_PLAYER(this)) { - if (STAT(FROZEN, this) == 2) + if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1); - SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); + SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); if (STAT(REVIVE_PROGRESS, this) >= 1) - Unfreeze(this); + Unfreeze(this, false); } - else if (STAT(FROZEN, this) == 3) + else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1); - SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); + SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); - if (GetResourceAmount(this, RESOURCE_HEALTH) < 1) + if (GetResource(this, RES_HEALTH) < 1) { if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -2428,7 +2448,7 @@ void PlayerPreThink (entity this) this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); } else if (STAT(REVIVE_PROGRESS, this) <= 0) - Unfreeze(this); + Unfreeze(this, false); } } @@ -2662,13 +2682,344 @@ void PlayerPostThink (entity this) } if (this.waypointsprite_attachedforcarrier) { - vector v = healtharmor_maxdamage(GetResourceAmount(this, RESOURCE_HEALTH), GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); - WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); - } + float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x; + WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, hp); + } CSQCMODEL_AUTOUPDATE(this); } +/** + * message "": do not say, just test flood control + * return value: + * 1 = accept + * 0 = reject + * -1 = fake accept + */ +int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) +{ + if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ") + msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) + + if (source) + msgin = formatmessage(source, msgin); + + string colorstr; + if (!(IS_PLAYER(source) || source.caplayer)) + colorstr = "^0"; // black for spectators + else if(teamplay) + colorstr = Team_ColorCode(source.team); + else + { + colorstr = ""; + teamsay = false; + } + + if (!source) { + colorstr = ""; + teamsay = false; + } + + if(msgin != "") + msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); + + /* + * using bprint solves this... me stupid + // how can we prevent the message from appearing in a listen server? + // for now, just give "say" back and only handle say_team + if(!teamsay) + { + clientcommand(source, strcat("say ", msgin)); + return; + } + */ + + string namestr = ""; + if (source) + namestr = playername(source, autocvar_g_chat_teamcolors); + + string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; + + string msgstr = "", cmsgstr = ""; + string privatemsgprefix = string_null; + int privatemsgprefixlen = 0; + if (msgin != "") + { + bool found_me = false; + if(strstrofs(msgin, "/me", 0) >= 0) + { + string newmsgin = ""; + string newnamestr = ((teamsay) ? strcat(colorstr, "(", colorprefix, namestr, colorstr, ")", "^7") : strcat(colorprefix, namestr, "^7")); + FOREACH_WORD(msgin, true, + { + if(strdecolorize(it) == "/me") + { + found_me = true; + newmsgin = cons(newmsgin, newnamestr); + } + else + newmsgin = cons(newmsgin, it); + }); + msgin = newmsgin; + } + + if(privatesay) + { + msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); + privatemsgprefixlen = strlen(msgstr); + msgstr = strcat(msgstr, msgin); + cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7"); + } + else if(teamsay) + { + if(found_me) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + //msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin); + msgstr = strcat("\{1}\{13}^4* ", "^7", msgin); + } + else + msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin); + cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin); + } + else + { + if(found_me) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + //msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); + msgstr = strcat("\{1}^4* ^7", msgin); + } + else { + msgstr = "\{1}"; + msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); + msgstr = strcat(msgstr, msgin); + } + cmsgstr = ""; + } + msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint + } + + string fullmsgstr = msgstr; + string fullcmsgstr = cmsgstr; + + // FLOOD CONTROL + int flood = 0; + var .float flood_field = floodcontrol_chat; + if(floodcontrol && source) + { + float flood_spl; + float flood_burst; + float flood_lmax; + float lines; + if(privatesay) + { + flood_spl = autocvar_g_chat_flood_spl_tell; + flood_burst = autocvar_g_chat_flood_burst_tell; + flood_lmax = autocvar_g_chat_flood_lmax_tell; + flood_field = floodcontrol_chattell; + } + else if(teamsay) + { + flood_spl = autocvar_g_chat_flood_spl_team; + flood_burst = autocvar_g_chat_flood_burst_team; + flood_lmax = autocvar_g_chat_flood_lmax_team; + flood_field = floodcontrol_chatteam; + } + else + { + flood_spl = autocvar_g_chat_flood_spl; + flood_burst = autocvar_g_chat_flood_burst; + flood_lmax = autocvar_g_chat_flood_lmax; + flood_field = floodcontrol_chat; + } + flood_burst = max(0, flood_burst - 1); + // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four! + + // do flood control for the default line size + if(msgstr != "") + { + getWrappedLine_remaining = msgstr; + msgstr = ""; + lines = 0; + while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax)) + { + msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width + ++lines; + } + msgstr = substring(msgstr, 1, strlen(msgstr) - 1); + + if(getWrappedLine_remaining != "") + { + msgstr = strcat(msgstr, "\n"); + flood = 2; + } + + if (time >= source.(flood_field)) + { + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; + } + else + { + flood = 1; + msgstr = fullmsgstr; + } + } + else + { + if (time >= source.(flood_field)) + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; + else + flood = 1; + } + + if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection + source.(flood_field) = flood = 0; + } + + string sourcemsgstr, sourcecmsgstr; + if(flood == 2) // cannot happen for empty msgstr + { + if(autocvar_g_chat_flood_notify_flooder) + { + sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n"); + sourcecmsgstr = ""; + } + else + { + sourcemsgstr = fullmsgstr; + sourcecmsgstr = fullcmsgstr; + } + cmsgstr = ""; + } + else + { + sourcemsgstr = msgstr; + sourcecmsgstr = cmsgstr; + } + + if (!privatesay && source && !(IS_PLAYER(source) || source.caplayer)) + { + if (!game_stopped) + if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage)) + teamsay = -1; // spectators + } + + if(flood) + LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding."); + + // build sourcemsgstr by cutting off a prefix and replacing it by the other one + if(privatesay) + sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); + + int ret; + if(source && CS(source).muted) + { + // always fake the message + ret = -1; + } + else if(flood == 1) + { + if (autocvar_g_chat_flood_notify_flooder) + { + sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); + ret = 0; + } + else + ret = -1; + } + else + { + ret = 1; + } + + if (privatesay && source && !(IS_PLAYER(source) || source.caplayer)) + { + if (!game_stopped) + if ((privatesay && (IS_PLAYER(privatesay) || privatesay.caplayer)) && ((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))) + ret = -1; // just hide the message completely + } + + MUTATOR_CALLHOOK(ChatMessage, source, ret); + ret = M_ARGV(1, int); + + string event_log_msg = ""; + + if(sourcemsgstr != "" && ret != 0) + { + if(ret < 0) // faked message, because the player is muted + { + sprint(source, sourcemsgstr); + if(sourcecmsgstr != "" && !privatesay) + centerprint(source, sourcecmsgstr); + } + else if(privatesay) // private message, between 2 people only + { + sprint(source, sourcemsgstr); + if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled + if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source)) + { + sprint(privatesay, msgstr); + if(cmsgstr != "") + centerprint(privatesay, cmsgstr); + } + } + else if ( teamsay && CS(source).active_minigame ) + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + event_log_msg = sprintf(":chat_minigame:%d:%s:%s", source.playerid, CS(source).active_minigame.netname, msgin); + + } + else if(teamsay > 0) // team message, only sent to team mates + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + if(sourcecmsgstr != "") + centerprint(source, sourcecmsgstr); + FOREACH_CLIENT((IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + if(cmsgstr != "") + centerprint(it, cmsgstr); + }); + event_log_msg = sprintf(":chat_team:%d:%d:%s", source.playerid, source.team, strreplace("\n", " ", msgin)); + } + else if(teamsay < 0) // spectator message, only sent to spectators + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(!(IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + event_log_msg = sprintf(":chat_spec:%d:%s", source.playerid, strreplace("\n", " ", msgin)); + } + else + { + if (source) { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + MX_Say(strcat(playername(source, true), "^7: ", msgin)); + } + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + event_log_msg = sprintf(":chat:%d:%s", source.playerid, strreplace("\n", " ", msgin)); + } + } + + if (autocvar_sv_eventlog && (event_log_msg != "")) { + GameLogEcho(event_log_msg); + } + + return ret; +} + // hack to copy the button fields from the client entity to the Client State void PM_UpdateButtons(entity this, entity store) { @@ -2676,7 +3027,7 @@ void PM_UpdateButtons(entity this, entity store) store.impulse = this.impulse; this.impulse = 0; - bool typing = this.buttonchat; + bool typing = this.buttonchat || this.button14; store.button0 = (typing) ? 0 : this.button0; //button1?!