- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
- make
- - EXPECT=fb771b180a47f7acf09eb12dddd391f8
+ - EXPECT=04ccb2566f85a56b1bfa956509a8b22a
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
-Sat Mar 17 07:23:46 CET 2018
+Fri Apr 20 07:23:54 CEST 2018
QCC=${QCC:-$PWD/../../gmqcc/gmqcc${CMAKE_EXECUTABLE_SUFFIX}}
case $1 in
compile)
- ${CPP} ${@:3} | sed -E 's/^#(line)? ([[:digit:]]+) "(.*)".*/'$'\\\n''#pragma file(\3)'$'\\\n''#pragma line(\2)/g' > $2
+ for var in "$@"; do case "$var" in
+ -I*)
+ home=${var:2}
+ break
+ ;;
+ esac; done
+ ${CPP} ${@:3} \
+ | sed -E "s|${home}|~|g" \
+ | sed -E 's/^#(line)? ([[:digit:]]+) "(.*)".*/'$'\\\n''#pragma file(\3)'$'\\\n''#pragma line(\2)/g' \
+ > $2
;;
link)
${QCC} \
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
-# Nicky Rowe <nickyrowe@gmail.com>, 2016
+# Nicky Rowe <nickyrowe688@gmail.com>, 2016
+# Nicky Rowe <nickyrowe688@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Xonotic\n"
# Kriss Chr <kriss7475@gmail.com>, 2017
# Piotr Kozica <koza91@gmail.com>, 2016
# Rafał Szymański <okavasly@gmail.com>, 2017
-# Robert Wolniak <robert.wolniak@gmail.com>, 2015
+# Robert Wolniak <robert.wolniak@gmail.com>, 2015,2018
# Sertomas, 2014
msgid ""
msgstr ""
"Project-Id-Version: Xonotic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-09 00:35+0200\n"
-"PO-Revision-Date: 2017-09-20 00:10+0000\n"
-"Last-Translator: Rafał Szymański <okavasly@gmail.com>\n"
+"PO-Revision-Date: 2018-04-19 09:01+0000\n"
+"Last-Translator: Robert Wolniak <robert.wolniak@gmail.com>\n"
"Language-Team: Polish (http://www.transifex.com/team-xonotic/xonotic/"
"language/pl/)\n"
"Language: pl\n"
#: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7
msgid "Ball Stealer"
-msgstr ""
+msgstr "ZÅ‚odziej Kuli"
#: qcsrc/common/items/item/armor.qh:111
msgid "Big armor"
"Kill enemies to freeze them, stand next to frozen teammates to revive them; "
"freeze all enemies to win"
msgstr ""
+"Zabijaj przeciwników by ich zamrozić i stój obok członków swojej drużyny by "
+"ich wskrzesić; aby wygrać, zamroź wszystkich wrogów"
#: qcsrc/common/mapinfo.qh:446
msgid "Hold the ball to get points for kills"
"You lost the game!\n"
"Select \"^1Next Match^7\" on the menu for a rematch!"
msgstr ""
+"Przegrana meczu!\n"
+"Wybierz w menu opcję \"^1Następny Mecz^7\" po rewanż!"
#: qcsrc/common/minigames/minigame/pp.qc:444
#: qcsrc/common/minigames/minigame/ttt.qc:325
"You win!\n"
"Select \"^1Next Match^7\" on the menu to start a new match!"
msgstr ""
+"Wygrana!\n"
+"Wybierz w menu opcję \"^1Następny Mecz^7\" by rozpocząć nową grę!"
#: qcsrc/common/minigames/minigame/pp.qc:450
#: qcsrc/common/minigames/minigame/ttt.qc:331
msgid "Select \"^1Next Match^7\" on the menu to start a new match!"
-msgstr ""
+msgstr "Wybierz w menu opcję \"^1Następny Mecz^7\" by rozpocząć nową grę!"
#: qcsrc/common/minigames/minigame/pp.qc:451
#: qcsrc/common/minigames/minigame/ttt.qc:332
msgid "Wait for your opponent to confirm the rematch"
-msgstr ""
+msgstr "Poczekaj aż twój przeciwnik potwierdzi rewanż."
#: qcsrc/common/minigames/minigame/pp.qc:582
#: qcsrc/common/minigames/minigame/ttt.qc:665
#
# Translators:
# Ivan Paulos Tomé <greylica@gmail.com>, 2016
-# Jean Trindade Pereira <jean_trindade2@hotmail.com>, 2015-2017
+# Jean Trindade Pereira <jean_trindade2@hotmail.com>, 2015-2018
# Mirio <opivy@hotmail.de>, 2017
# NotThatPrivate Yes <henriqueferreira2009@gmail.com>, 2015
# Ricardo Manuel da Cruz Coelho da Silva <ricardo.mccs@gmail.com>, 2015
"Project-Id-Version: Xonotic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-09 00:35+0200\n"
-"PO-Revision-Date: 2017-12-21 15:23+0000\n"
+"PO-Revision-Date: 2018-04-17 20:43+0000\n"
"Last-Translator: Jean Trindade Pereira <jean_trindade2@hotmail.com>\n"
"Language-Team: Portuguese (http://www.transifex.com/team-xonotic/xonotic/"
"language/pt/)\n"
"^3caps^7 How often a flag (CTF) or a key (KeyHunt) was "
"captured\n"
msgstr ""
-"^3capturas^7 Quão frequente uma bandeira (CTF) ou uma chave (KeyHunt) foi "
-"capturada\n"
+"^3capturas^7 Quão frequente uma bandeira (CTF) ou uma chave (Caça a Chaves) "
+"foi capturada\n"
#: qcsrc/client/hud/panel/scoreboard.qc:317
msgid ""
"^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a "
"ball (Keepaway) was picked up\n"
msgstr ""
-"^3coletas^7. Quão frequente uma bandeira (CTF), uma chave (KeyHunt) ou uma "
-"bola (Keepaway) foi coletada\n"
+"^3coletas^7. Quão frequente uma bandeira (CTF), uma chave (Caça a Chaves) ou "
+"uma bola (Keepaway) foi coletada\n"
#: qcsrc/client/hud/panel/scoreboard.qc:318
msgid "^3captime^7 Time of fastest cap (CTF)\n"
#: qcsrc/common/mapinfo.qh:111
msgid "Last Man Standing"
-msgstr "Last Man Standing"
+msgstr "Último Homem de Pé"
#: qcsrc/common/mapinfo.qh:111
msgid "Survive and kill until the enemies have no lives left"
#: qcsrc/common/mapinfo.qh:249
msgid "Clan Arena"
-msgstr "Clan Arena"
+msgstr "Clã Arena"
#: qcsrc/common/mapinfo.qh:249
msgid "Kill all enemy teammates to win the round"
#: qcsrc/common/mapinfo.qh:287
msgid "Domination"
-msgstr "Domination"
+msgstr "Dominação"
#: qcsrc/common/mapinfo.qh:319
msgid "Gather all the keys to win the round"
#: qcsrc/common/mapinfo.qh:319
msgid "Key Hunt"
-msgstr "Key Hunt"
+msgstr "Caça a Chaves"
#: qcsrc/common/mapinfo.qh:353
msgid "Assault"
-msgstr "Assault"
+msgstr "Assalto"
#: qcsrc/common/mapinfo.qh:353
msgid ""
#: qcsrc/common/mapinfo.qh:371
msgid "Onslaught"
-msgstr "Onslaught"
+msgstr "Massacre"
#: qcsrc/common/mapinfo.qh:387
msgid "Nexball"
-msgstr "Nexball"
+msgstr "Bola Nex"
#: qcsrc/common/mapinfo.qh:387
msgid "Shoot and kick the ball into the enemies goal, keep your goal clean"
#: qcsrc/common/mapinfo.qh:408
msgid "Freeze Tag"
-msgstr "Freeze Tag"
+msgstr "Congela"
#: qcsrc/common/mapinfo.qh:408
msgid ""
#: qcsrc/common/mapinfo.qh:461
msgid "Invasion"
-msgstr "Invasion"
+msgstr "Invasão"
#: qcsrc/common/mapinfo.qh:461
msgid "Survive against waves of monsters"
#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:25
msgid "Flip messages order"
-msgstr "Trocar ordem de notificações"
+msgstr "Trocar ordem de mensagens"
#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:27
#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:15
#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:68
msgid "Ammo bar alpha:"
-msgstr "Cor da barra de munições:"
+msgstr "Transparência da barra de munições:"
#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:74
msgid "Ammo bar color:"
#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:204
msgid "Grappling hook"
-msgstr "Gancho (grappling hook)"
+msgstr "Gancho de escalada"
#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:205
msgid "Players spawn with the grappling hook"
// Veil (9)
set g_nades_veil_time 8 "Life time of the orb"
-set g_nades_veil_radius 300
+set g_nades_veil_radius 200
// ============
if(monster == "random" || allow_any)
{
RandomSelection_Init();
- FOREACH(Monsters, it != MON_Null && (allow_any || (!(it.spawnflags & MONSTER_TYPE_PASSIVE) && !(it.spawnflags & MON_FLAG_HIDDEN))),
+ FOREACH(Monsters, it != MON_Null && (allow_any || !(it.spawnflags & MON_FLAG_HIDDEN)) && !(it.spawnflags & MONSTER_TYPE_PASSIVE),
{
RandomSelection_AddEnt(it, 1, 1);
});
BUFF_ONADD(BUFF_INVISIBLE)
{
- if(time < player.strength_finished && g_instagib)
+ if(time < player.strength_finished && autocvar_g_instagib)
player.buff_invisible_prev_alpha = default_player_alpha; // we don't want to save the powerup's alpha, as player may lose the powerup while holding the buff
else
player.buff_invisible_prev_alpha = player.alpha;
BUFF_ONREM(BUFF_INVISIBLE)
{
- if(time < player.strength_finished && g_instagib)
+ if(time < player.strength_finished && autocvar_g_instagib)
player.alpha = autocvar_g_instagib_invis_alpha;
else
player.alpha = player.buff_invisible_prev_alpha;
#include "sv_cloaked.qh"
-string autocvar_g_cloaked;
-REGISTER_MUTATOR(cloaked, expr_evaluate(autocvar_g_cloaked));
+//string autocvar_g_cloaked;
+REGISTER_MUTATOR(cloaked, expr_evaluate(cvar_string("g_cloaked")));
float autocvar_g_balance_cloaked_alpha;
#include "sv_melee_only.qh"
string autocvar_g_melee_only;
-REGISTER_MUTATOR(melee_only, expr_evaluate(autocvar_g_melee_only) && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball);
+REGISTER_MUTATOR(melee_only, expr_evaluate(autocvar_g_melee_only) && !autocvar_g_instagib && !cvar("g_overkill") && !g_nexball);
MUTATOR_HOOKFUNCTION(melee_only, SetStartItems, CBC_ORDER_LAST)
{
*/
-string autocvar_g_new_toys;
+//string autocvar_g_new_toys;
bool nt_IsNewToy(int w);
-REGISTER_MUTATOR(nt, expr_evaluate(autocvar_g_new_toys) && !cvar("g_instagib") && !cvar("g_overkill"))
+REGISTER_MUTATOR(nt, expr_evaluate(cvar_string("g_new_toys")) && !autocvar_g_instagib && !cvar("g_overkill"))
{
MUTATOR_ONADD
{
#include "sv_nix.qh"
-string autocvar_g_nix;
+//string autocvar_g_nix;
int autocvar_g_balance_nix_ammo_cells;
int autocvar_g_balance_nix_ammo_plasma;
int autocvar_g_balance_nix_ammo_fuel;
bool NIX_CanChooseWeapon(int wpn);
-REGISTER_MUTATOR(nix, expr_evaluate(autocvar_g_nix) && !cvar("g_instagib") && !cvar("g_overkill"))
+REGISTER_MUTATOR(nix, expr_evaluate(cvar_string("g_nix")) && !autocvar_g_instagib && !cvar("g_overkill"))
{
MUTATOR_ONADD
{
.Weapon ok_lastwep[MAX_WEAPONSLOTS];
-REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
+REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !autocvar_g_instagib && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
{
MUTATOR_ONADD
{
#include "sv_pinata.qh"
string autocvar_g_pinata;
-REGISTER_MUTATOR(pinata, expr_evaluate(autocvar_g_pinata) && !cvar("g_instagib") && !cvar("g_overkill"));
+REGISTER_MUTATOR(pinata, expr_evaluate(autocvar_g_pinata) && !autocvar_g_instagib && !cvar("g_overkill"));
MUTATOR_HOOKFUNCTION(pinata, PlayerDies)
{
#include <common/deathtypes/all.qh>
#include <server/round_handler.qh>
-REGISTER_MUTATOR(rm, cvar("g_instagib"));
+REGISTER_MUTATOR(rm, autocvar_g_instagib);
MUTATOR_HOOKFUNCTION(rm, Damage_Calculate)
{
#include "sv_vampire.qh"
string autocvar_g_vampire;
-REGISTER_MUTATOR(vampire, expr_evaluate(autocvar_g_vampire) && !cvar("g_instagib"));
+REGISTER_MUTATOR(vampire, expr_evaluate(autocvar_g_vampire) && !autocvar_g_instagib);
MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
{
STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
STAT(MOVEVARS_JUMPVELOCITY_CROUCH, this) = Physics_ClientOption(this, "jumpvelocity_crouch", autocvar_sv_jumpvelocity_crouch);
STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
+
+ MUTATOR_CALLHOOK(PlayerPhysics_PostUpdateStats, this, maxspd_mod);
}
#endif
// const int CH_BGM_SINGLE = -8;
const int CH_BGM_SINGLE = 8;
const int CH_AMBIENT = -9;
-// const int CH_AMBIENT_SINGLE = 9;
+const int CH_AMBIENT_SINGLE = 9;
const float ATTEN_NONE = 0;
const float ATTEN_MIN = 0.015625;
strfree(this.mdl);
}
+HashMap ENT_CLIENT_ITEM_simple;
+STATIC_INIT(ENT_CLIENT_ITEM_simple)
+{
+ HM_NEW(ENT_CLIENT_ITEM_simple);
+}
+SHUTDOWN(ENT_CLIENT_ITEM_simple)
+{
+ HM_DELETE(ENT_CLIENT_ITEM_simple);
+}
+
NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
{
int sf = ReadByte();
strfree(this.mdl);
- this.mdl = "";
string _fn = ReadString();
this.item_simple = false; // reset it!
string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
this.item_simple = true;
- if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3")))
- this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3"));
- else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm")))
- this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm"));
- else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm")))
- this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm"));
- else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl")))
- this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl"));
- else
- {
- this.item_simple = false;
- LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it");
- }
+ #define extensions(x) \
+ x(md3) \
+ x(dpm) \
+ x(iqm) \
+ x(mdl) \
+ /**/
+ #define tryext(ext) { \
+ string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \
+ string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \
+ if (cached == "") { \
+ HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \
+ } \
+ if (cached != "0") { \
+ strcpy(this.mdl, s); \
+ break; \
+ } \
+ }
+ do {
+ extensions(tryext);
+ this.item_simple = false;
+ LOG_TRACEF("Simple item requested for %s but no model exists for it", _fn);
+ } while (0);
+ #undef tryext
+ #undef extensions
}
if(!this.item_simple)
- this.mdl = strzone(_fn);
-
+ strcpy(this.mdl, _fn);
if(this.mdl == "")
- LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!");
+ LOG_WARNF("this.mdl is unset for item %s", this.classname);
precache_model(this.mdl);
_setmodel(this, this.mdl);
--- /dev/null
+#pragma once
+
+//-----------
+// SPAWNFLAGS
+//-----------
+const int START_ENABLED = BIT(0);
+const int START_DISABLED = BIT(0);
+const int ALL_ENTITIES = BIT(1);
+const int ON_MAPLOAD = BIT(1);
+const int INVERT_TEAMS = BIT(2);
+const int CRUSH = BIT(2);
+const int NOSPLASH = BIT(8); // generic anti-splashdamage spawnflag
+const int ONLY_PLAYERS = BIT(14);
+
+// triggers
+const int SPAWNFLAG_NOMESSAGE = BIT(0);
+const int SPAWNFLAG_NOTOUCH = BIT(0);
+
+//----------
+// SENDFLAGS
+//----------
+const int SF_TRIGGER_INIT = BIT(0);
+const int SF_TRIGGER_UPDATE = BIT(1);
+const int SF_TRIGGER_RESET = BIT(2);
+
+//----------------
+// STATES & ACTIVE
+//----------------
+#ifdef CSQC
+// this stuff is defined in the server side engine VM, so we must define it separately here
+const int STATE_TOP = 0;
+const int STATE_BOTTOM = 1;
+const int STATE_UP = 2;
+const int STATE_DOWN = 3;
+
+const int ACTIVE_NOT = 0;
+const int ACTIVE_ACTIVE = 1;
+const int ACTIVE_IDLE = 2;
+const int ACTIVE_BUSY = 2;
+const int ACTIVE_TOGGLE = 3;
+#endif
this.dmgtime2 = time;
// how far to bob
- if (this.spawnflags & 1) // X
+ if (this.spawnflags & BOBBING_XAXIS)
this.movedir = '1 0 0' * this.height;
- else if (this.spawnflags & 2) // Y
+ else if (this.spawnflags & BOBBING_YAXIS)
this.movedir = '0 1 0' * this.height;
else // Z
this.movedir = '0 0 1' * this.height;
#pragma once
+
+
+const int BOBBING_XAXIS = BIT(0);
+const int BOBBING_YAXIS = BIT(1);
// target = targets to trigger when broken
// health = amount of damage it can take
// spawnflags:
-// 1 = start disabled (needs to be triggered to activate)
-// 2 = indicate damage
-// 4 = don't take direct damage (needs to be triggered to 'explode', then triggered again to restore)
+// START_DISABLED: needs to be triggered to activate
+// BREAKABLE_INDICATE_DAMAGE: indicate damage
+// BREAKABLE_NODAMAGE: don't take direct damage (needs to be triggered to 'explode', then triggered again to restore)
+// NOSPLASH: don't take splash damage
// notes:
// for mdl_dead to work, origin must be set (using a common/origin brush).
// Otherwise mdl_dead will be displayed at the map origin, and nobody would
void func_breakable_colormod(entity this)
{
float h;
- if (!(this.spawnflags & 2))
+ if (!(this.spawnflags & BREAKABLE_INDICATE_DAMAGE))
return;
h = this.health / this.max_health;
if(h < 0.25)
IL_REMOVE(g_bot_targets, this);
this.bot_attack = false;
this.event_damage = func_null;
- this.state = 1;
- if(this.spawnflags & 4)
+ this.state = STATE_BROKEN;
+ if(this.spawnflags & BREAKABLE_NODAMAGE)
this.use = func_null;
func_breakable_colormod(this);
if (this.noise1)
WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health);
WaypointSprite_UpdateHealth(this.sprite, this.health);
}
- if(!(this.spawnflags & 4))
+ if(!(this.spawnflags & BREAKABLE_NODAMAGE))
{
this.takedamage = DAMAGE_AIM;
if(!this.bot_attack)
this.bot_attack = true;
this.event_damage = func_breakable_damage;
}
- if(this.spawnflags & 4)
+ if(this.spawnflags & BREAKABLE_NODAMAGE)
this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset
- this.state = 0;
+ this.state = STATE_ALIVE;
//this.nextthink = 0; // cancel auto respawn
setthink(this, func_breakable_think);
this.nextthink = time + 0.1;
void func_breakable_init_for_player(entity this, entity player)
{
- if (this.noise1 && this.state == 0 && IS_REAL_CLIENT(player))
+ if (this.noise1 && this.state == STATE_ALIVE && IS_REAL_CLIENT(player))
{
msg_entity = player;
soundto (MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
{
- if(this.state == 1)
+ if(this.state == STATE_BROKEN)
return;
- if(this.spawnflags & DOOR_NOSPLASH)
+ if(this.spawnflags & NOSPLASH)
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
if(this.team)
{
this.team = this.team_saved;
func_breakable_look_restore(this);
- if(this.spawnflags & 1)
+ if(this.spawnflags & START_DISABLED)
func_breakable_behave_destroyed(this);
else
func_breakable_behave_restore(this);
this.mdl = this.model;
SetBrushEntityModel(this);
- if(this.spawnflags & 4)
+ if(this.spawnflags & BREAKABLE_NODAMAGE)
this.use = func_breakable_destroy;
else
this.use = func_breakable_restore;
- if(this.spawnflags & 4)
+ if(this.spawnflags & BREAKABLE_NODAMAGE)
{
this.takedamage = DAMAGE_NO;
this.event_damage = func_null;
#pragma once
+
+const int BREAKABLE_INDICATE_DAMAGE = BIT(1);
+const int BREAKABLE_NODAMAGE = BIT(2);
+
+const int STATE_ALIVE = 0;
+const int STATE_BROKEN = 1;
+
#ifdef SVQC
spawnfunc(func_breakable);
#endif
void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
{
- if(this.spawnflags & DOOR_NOSPLASH)
+ if(this.spawnflags & NOSPLASH)
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
"wait" override the default 1 second wait (-1 = never return)
"lip" override the default 4 pixel lip remaining at end of move
"health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser
-"sounds"
-0) steam metal
-1) wooden clunk
-2) metallic click
-3) in-out
+"noise" sound that is played when the button is activated
*/
spawnfunc(func_button)
{
#pragma once
-const int BUTTON_DONTACCUMULATEDMG = 128;
+
+const int BUTTON_DONTACCUMULATEDMG = BIT(7);
IL_REMOVE(g_conveyed, it);
});
- if(this.state)
+ if(this.active == ACTIVE_ACTIVE)
{
- FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, !it.conveyor.state && isPushable(it),
+ FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.conveyor.active == ACTIVE_NOT && isPushable(it),
{
vector emin = it.absmin;
vector emax = it.absmax;
#ifdef SVQC
-void conveyor_use(entity this, entity actor, entity trigger)
-{
- this.state = !this.state;
-
- this.SendFlags |= 2;
-}
-
-void conveyor_reset(entity this)
-{
- this.state = (this.spawnflags & 1);
-
- this.SendFlags |= 2;
-}
-
-bool conveyor_send(entity this, entity to, int sf)
+bool conveyor_send(entity this, entity to, int sendflags)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR);
- WriteByte(MSG_ENTITY, sf);
+ WriteByte(MSG_ENTITY, sendflags);
- if(sf & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
WriteByte(MSG_ENTITY, this.warpzone_isboxy);
WriteVector(MSG_ENTITY, this.origin);
WriteVector(MSG_ENTITY, this.movedir);
WriteByte(MSG_ENTITY, this.speed);
- WriteByte(MSG_ENTITY, this.state);
+ WriteByte(MSG_ENTITY, this.active);
WriteString(MSG_ENTITY, this.targetname);
WriteString(MSG_ENTITY, this.target);
}
- if(sf & 2)
- WriteByte(MSG_ENTITY, this.state);
+ if(sendflags & SF_TRIGGER_UPDATE)
+ WriteByte(MSG_ENTITY, this.active);
return true;
}
this.movedir *= this.speed;
setthink(this, conveyor_think);
this.nextthink = time;
+ this.setactive = generic_netlinked_setactive;
IFTARGETED
{
- this.use = conveyor_use;
- this.reset = conveyor_reset;
- this.reset(this);
+ // backwards compatibility
+ this.use = generic_netlinked_legacy_use;
}
- else
- this.state = 1;
+ this.reset = generic_netlinked_reset;
+ this.reset(this);
FixSize(this);
Net_LinkEntity(this, 0, false, conveyor_send);
- this.SendFlags |= 1;
+ this.SendFlags |= SF_TRIGGER_INIT;
}
spawnfunc(trigger_conveyor)
NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew)
{
- int sf = ReadByte();
+ int sendflags = ReadByte();
- if(sf & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
this.warpzone_isboxy = ReadByte();
this.origin = ReadVector();
this.movedir = ReadVector();
this.speed = ReadByte();
- this.state = ReadByte();
+ this.active = ReadByte();
this.targetname = strzone(ReadString());
this.target = strzone(ReadString());
conveyor_init(this, isnew);
}
- if(sf & 2)
- this.state = ReadByte();
+ if(sendflags & SF_TRIGGER_UPDATE)
+ this.active = ReadByte();
return true;
}
#pragma once
+#include "../defs.qh"
IntrusiveList g_conveyed;
STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); }
#include "door.qh"
+#include "door_rotating.qh"
/*
Doors are similar to buttons, but can spawn a fat trigger field around them
void door_go_down(entity this);
void door_go_up(entity this, entity actor, entity trigger);
-void door_rotating_go_down(entity this);
-void door_rotating_go_up(entity this, entity oth);
void door_blocked(entity this, entity blocker)
{
- if((this.spawnflags & 8)
+ if((this.spawnflags & DOOR_CRUSH)
#ifdef SVQC
&& (blocker.takedamage != DAMAGE_NO)
#elif defined(CSQC)
if(!door.itemkeys)
{
#ifdef SVQC
- play2(player, SND(TALK));
+ play2(player, door.noise);
Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED);
#endif
return true;
door_go_up(e, actor, trigger);
} else {
// if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
- if ((e.spawnflags & 2) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) {
+ if ((e.spawnflags & DOOR_ROTATING_BIDIR) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) {
e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating
e.pos2 = '0 0 0' - e.pos2;
}
// if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side
- if (!((e.spawnflags & 2) && (e.spawnflags & 8) && e.state == STATE_DOWN
+ if (!((e.spawnflags & DOOR_ROTATING_BIDIR) && (e.spawnflags & DOOR_ROTATING_BIDIR_IN_DOWN) && e.state == STATE_DOWN
&& (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0)))))
{
door_rotating_go_up(e, trigger);
void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
{
- if(this.spawnflags & DOOR_NOSPLASH)
+ if(this.spawnflags & NOSPLASH)
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
this.health = this.health - damage;
void door_generic_plat_blocked(entity this, entity blocker)
{
- if((this.spawnflags & 8) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!!
+ if((this.spawnflags & DOOR_CRUSH) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!!
#ifdef SVQC
Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0');
#endif
}
}
-void door_rotating_hit_top(entity this)
-{
- if (this.noise1 != "")
- _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
- this.state = STATE_TOP;
- if (this.spawnflags & DOOR_TOGGLE)
- return; // don't come down automatically
- setthink(this, door_rotating_go_down);
- this.nextthink = this.ltime + this.wait;
-}
-
-void door_rotating_hit_bottom(entity this)
-{
- if (this.noise1 != "")
- _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
- if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating
- {
- this.pos2 = '0 0 0' - this.pos2;
- this.lip = 0;
- }
- this.state = STATE_BOTTOM;
-}
-
-void door_rotating_go_down(entity this)
-{
- if (this.noise2 != "")
- _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM);
- if (this.max_health)
- {
- this.takedamage = DAMAGE_YES;
- this.health = this.max_health;
- }
-
- this.state = STATE_DOWN;
- SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom);
-}
-
-void door_rotating_go_up(entity this, entity oth)
-{
- if (this.state == STATE_UP)
- return; // already going up
-
- if (this.state == STATE_TOP)
- { // reset top wait time
- this.nextthink = this.ltime + this.wait;
- return;
- }
- if (this.noise2 != "")
- _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM);
- this.state = STATE_UP;
- SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top);
-
- string oldmessage;
- oldmessage = this.message;
- this.message = "";
- SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here?
- this.message = oldmessage;
-}
-
-
/*
=========================================
door trigger
entity LinkDoors_nextent(entity cur, entity near, entity pass)
{
- while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+ while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & DOOR_DONT_LINK) || cur.enemy))
{
}
return cur;
if (this.enemy)
return; // already linked by another door
- if (this.spawnflags & 4)
+ if (this.spawnflags & DOOR_DONT_LINK)
{
this.owner = this.enemy = this;
#ifdef SVQC
-// spawnflags require key (for now only func_door)
-spawnfunc(func_door)
+// common code for func_door and func_door_rotating spawnfuncs
+void door_init_shared(entity this)
{
- // Quake 1 keys compatibility
- if (this.spawnflags & SPAWNFLAGS_GOLD_KEY)
- this.itemkeys |= ITEM_KEY_BIT(0);
- if (this.spawnflags & SPAWNFLAGS_SILVER_KEY)
- this.itemkeys |= ITEM_KEY_BIT(1);
-
- SetMovedir(this);
-
this.max_health = this.health;
- if (!InitMovingBrushTrigger(this))
- return;
- this.effects |= EF_LOWPRECISION;
- this.classname = "door";
+ // unlock sound
if(this.noise == "")
+ {
this.noise = "misc/talk.wav";
+ }
+ // door still locked sound
if(this.noise3 == "")
+ {
this.noise3 = "misc/talk.wav";
+ }
precache_sound(this.noise);
precache_sound(this.noise3);
- setblocked(this, door_blocked);
- this.use = door_use;
-
- if(this.dmg && (this.message == ""))
+ if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message == ""))
+ {
this.message = "was squished";
- if(this.dmg && (this.message2 == ""))
+ }
+ if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message2 == ""))
+ {
this.message2 = "was squished by";
+ }
+ // TODO: other soundpacks
if (this.sounds > 0)
{
this.noise2 = "plats/medplat1.wav";
this.noise1 = "plats/medplat2.wav";
}
- if(this.noise1 && this.noise1 != "") { precache_sound(this.noise1); }
- if(this.noise2 && this.noise2 != "") { precache_sound(this.noise2); }
+ // sound when door stops moving
+ if(this.noise1 && this.noise1 != "")
+ {
+ precache_sound(this.noise1);
+ }
+ // sound when door is moving
+ if(this.noise2 && this.noise2 != "")
+ {
+ precache_sound(this.noise2);
+ }
- if (!this.speed)
- this.speed = 100;
if (!this.wait)
+ {
this.wait = 3;
+ }
if (!this.lip)
+ {
this.lip = 8;
+ }
+
+ this.state = STATE_BOTTOM;
+
+ if (this.health)
+ {
+ //this.canteamdamage = true; // TODO
+ this.takedamage = DAMAGE_YES;
+ this.event_damage = door_damage;
+ }
+
+ if (this.items)
+ {
+ this.wait = -1;
+ }
+}
+
+// spawnflags require key (for now only func_door)
+spawnfunc(func_door)
+{
+ // Quake 1 keys compatibility
+ if (this.spawnflags & SPAWNFLAGS_GOLD_KEY)
+ this.itemkeys |= ITEM_KEY_BIT(0);
+ if (this.spawnflags & SPAWNFLAGS_SILVER_KEY)
+ this.itemkeys |= ITEM_KEY_BIT(1);
+
+ SetMovedir(this);
+
+ if (!InitMovingBrushTrigger(this))
+ return;
+ this.effects |= EF_LOWPRECISION;
+ this.classname = "door";
+
+ setblocked(this, door_blocked);
+ this.use = door_use;
this.pos1 = this.origin;
this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip);
if (this.spawnflags & DOOR_START_OPEN)
InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION);
- this.state = STATE_BOTTOM;
+ door_init_shared(this);
- if (this.health)
+ if (!this.speed)
{
- //this.canteamdamage = true; // TODO
- this.takedamage = DAMAGE_YES;
- this.event_damage = door_damage;
+ this.speed = 100;
}
- if (this.items)
- this.wait = -1;
-
settouch(this, door_touch);
// LinkDoors can't be done until all of the doors have been spawned, so
#pragma once
-// door constants
-const int DOOR_START_OPEN = 1;
-const int DOOR_DONT_LINK = 4;
-const int DOOR_TOGGLE = 32;
-const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag
+const int DOOR_START_OPEN = BIT(0);
+const int DOOR_DONT_LINK = BIT(2);
+const int SPAWNFLAGS_GOLD_KEY = BIT(3); // Quake 1 compat, can only be used with func_door!
+const int SPAWNFLAGS_SILVER_KEY = BIT(4); // Quake 1 compat, can only be used with func_door!
+const int DOOR_TOGGLE = BIT(5);
-const int DOOR_NONSOLID = 1024;
+const int DOOR_NONSOLID = BIT(10);
+const int DOOR_CRUSH = BIT(11); // can't use CRUSH cause that is the same as DOOR_DONT_LINK
-const int SPAWNFLAGS_GOLD_KEY = 8;
-const int SPAWNFLAGS_SILVER_KEY = 16;
#ifdef CSQC
// stuff for preload
#include "door_rotating.qh"
-#ifdef SVQC
/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS
if two doors touch, they are assumed to be connected and operate as a unit.
FIXME: only one sound set available at the time being
*/
+#ifdef GAMEQC
+void door_rotating_hit_top(entity this)
+{
+ if (this.noise1 != "")
+ _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
+ this.state = STATE_TOP;
+ if (this.spawnflags & DOOR_TOGGLE)
+ return; // don't come down automatically
+ setthink(this, door_rotating_go_down);
+ this.nextthink = this.ltime + this.wait;
+}
+
+void door_rotating_hit_bottom(entity this)
+{
+ if (this.noise1 != "")
+ _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
+ if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating
+ {
+ this.pos2 = '0 0 0' - this.pos2;
+ this.lip = 0;
+ }
+ this.state = STATE_BOTTOM;
+}
+
+void door_rotating_go_down(entity this)
+{
+ if (this.noise2 != "")
+ _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM);
+ if (this.max_health)
+ {
+ this.takedamage = DAMAGE_YES;
+ this.health = this.max_health;
+ }
+
+ this.state = STATE_DOWN;
+ SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom);
+}
+
+void door_rotating_go_up(entity this, entity oth)
+{
+ if (this.state == STATE_UP)
+ return; // already going up
+
+ if (this.state == STATE_TOP)
+ { // reset top wait time
+ this.nextthink = this.ltime + this.wait;
+ return;
+ }
+ if (this.noise2 != "")
+ _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM);
+ this.state = STATE_UP;
+ SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top);
+
+ string oldmessage;
+ oldmessage = this.message;
+ this.message = "";
+ SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here?
+ this.message = oldmessage;
+}
+#endif
+
+#ifdef SVQC
void door_rotating_reset(entity this)
{
this.angles = this.pos1;
this.pos1 = this.movedir;
}
-
spawnfunc(func_door_rotating)
{
-
//if (!this.deathtype) // map makers can override this
// this.deathtype = " got in the way";
// I abuse "movedir" for denoting the axis for now
- if (this.spawnflags & 64) // X (untested)
+ if (this.spawnflags & DOOR_ROTATING_XAXIS)
this.movedir = '0 0 1';
- else if (this.spawnflags & 128) // Y (untested)
+ else if (this.spawnflags & DOOR_ROTATING_YAXIS)
this.movedir = '1 0 0';
else // Z
this.movedir = '0 1 0';
this.movedir = this.movedir * this.angles_y;
this.angles = '0 0 0';
- this.max_health = this.health;
this.avelocity = this.movedir;
if (!InitMovingBrushTrigger(this))
return;
setblocked(this, door_blocked);
this.use = door_use;
- if(this.spawnflags & 8)
- this.dmg = 10000;
-
- if(this.dmg && (this.message == ""))
- this.message = "was squished";
- if(this.dmg && (this.message2 == ""))
- this.message2 = "was squished by";
-
- if (this.sounds > 0)
- {
- precache_sound ("plats/medplat1.wav");
- precache_sound ("plats/medplat2.wav");
- this.noise2 = "plats/medplat1.wav";
- this.noise1 = "plats/medplat2.wav";
- }
-
- if (!this.speed)
- this.speed = 50;
- if (!this.wait)
- this.wait = 1;
- this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating
-
this.pos1 = '0 0 0';
this.pos2 = this.movedir;
if (this.spawnflags & DOOR_START_OPEN)
InitializeEntity(this, door_rotating_init_startopen, INITPRIO_SETLOCATION);
- this.state = STATE_BOTTOM;
-
- if (this.health)
+ door_init_shared(this);
+ if (!this.speed)
{
- //this.canteamdamage = true; // TODO
- this.takedamage = DAMAGE_YES;
- this.event_damage = door_damage;
+ this.speed = 50;
}
-
- if (this.items)
- this.wait = -1;
+ this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating
settouch(this, door_touch);
#pragma once
+
+
+const int DOOR_ROTATING_BIDIR = BIT(1);
+const int DOOR_ROTATING_BIDIR_IN_DOWN = BIT(3);
+
+const int DOOR_ROTATING_XAXIS = BIT(6);
+const int DOOR_ROTATING_YAXIS = BIT(7);
+
+
+#ifdef GAMEQC
+void door_rotating_go_down(entity this);
+void door_rotating_go_up(entity this, entity oth);
+#endif
void fd_secret_move6(entity this);
void fd_secret_done(entity this);
-const float SECRET_OPEN_ONCE = 1; // stays open
-const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
-const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
-const float SECRET_NO_SHOOT = 8; // only opened by trigger
-const float SECRET_YES_SHOOT = 16; // shootable even if targeted
-
void fd_secret_use(entity this, entity actor, entity trigger)
{
float temp;
_sound(this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
this.nextthink = this.ltime + 0.1;
- temp = 1 - (this.spawnflags & SECRET_1ST_LEFT); // 1 or -1
+ temp = 1 - (this.spawnflags & DOOR_SECRET_1ST_LEFT); // 1 or -1
makevectors(this.mangle);
if (!this.t_width)
{
- if (this.spawnflags & SECRET_1ST_DOWN)
+ if (this.spawnflags & DOOR_SECRET_1ST_DOWN)
this.t_width = fabs(v_up * this.size);
else
this.t_width = fabs(v_right * this.size);
if (!this.t_length)
this.t_length = fabs(v_forward * this.size);
- if (this.spawnflags & SECRET_1ST_DOWN)
+ if (this.spawnflags & DOOR_SECRET_1ST_DOWN)
this.dest1 = this.origin - v_up * this.t_width;
else
this.dest1 = this.origin + v_right * (this.t_width * temp);
{
if (this.noise3 != "")
_sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM);
- if (!(this.spawnflags & SECRET_OPEN_ONCE))
+ if (!(this.spawnflags & DOOR_SECRET_OPEN_ONCE) && this.wait >= 0)
{
this.nextthink = this.ltime + this.wait;
setthink(this, fd_secret_move4);
void fd_secret_done(entity this)
{
- if (this.spawnflags&SECRET_YES_SHOOT)
+ if (this.spawnflags&DOOR_SECRET_YES_SHOOT)
{
this.health = 10000;
this.takedamage = DAMAGE_YES;
void secret_reset(entity this)
{
- if (this.spawnflags & SECRET_YES_SHOOT)
+ if (this.spawnflags & DOOR_SECRET_YES_SHOOT)
{
this.health = 10000;
this.takedamage = DAMAGE_YES;
/*if (!this.deathtype) // map makers can override this
this.deathtype = " got in the way";*/
- if (!this.dmg) this.dmg = 2;
+ if (!this.dmg)
+ {
+ this.dmg = 2;
+ }
// Magic formula...
this.mangle = this.angles;
if (!InitMovingBrushTrigger(this)) return;
this.effects |= EF_LOWPRECISION;
- if (this.noise == "") this.noise = "misc/talk.wav";
+ // TODO: other soundpacks
+ if (this.sounds > 0)
+ {
+ this.noise1 = "plats/medplat1.wav";
+ this.noise2 = "plats/medplat1.wav";
+ this.noise3 = "plats/medplat2.wav";
+ }
+
+ // sound on touch
+ if (this.noise == "")
+ {
+ this.noise = "misc/talk.wav";
+ }
precache_sound(this.noise);
+ // sound while moving backwards
+ if (this.noise1 && this.noise1 != "")
+ {
+ precache_sound(this.noise1);
+ }
+ // sound while moving sideways
+ if (this.noise2 && this.noise2 != "")
+ {
+ precache_sound(this.noise2);
+ }
+ // sound when door stops moving
+ if (this.noise3 && this.noise3 != "")
+ {
+ precache_sound(this.noise3);
+ }
settouch(this, secret_touch);
setblocked(this, secret_blocked);
{
}
else
- this.spawnflags |= SECRET_YES_SHOOT;
+ this.spawnflags |= DOOR_SECRET_YES_SHOOT;
- if (this.spawnflags & SECRET_YES_SHOOT)
+ if (this.spawnflags & DOOR_SECRET_YES_SHOOT)
{
//this.canteamdamage = true; // TODO
this.health = 10000;
#pragma once
+
+
+const int DOOR_SECRET_OPEN_ONCE = BIT(0); // stays open - LEGACY, set wait to -1 instead
+const int DOOR_SECRET_1ST_LEFT = BIT(1); // 1st move is left of arrow
+const int DOOR_SECRET_1ST_DOWN = BIT(2); // 1st move is down from arrow
+const int DOOR_SECRET_NO_SHOOT = BIT(3); // only opened by trigger
+const int DOOR_SECRET_YES_SHOOT = BIT(4); // shootable even if targeted
spawnfunc(func_plat)
{
- if (this.sounds == 0) this.sounds = 2;
-
- if (this.spawnflags & 4) this.dmg = 10000;
+ if (this.spawnflags & CRUSH)
+ {
+ this.dmg = 10000;
+ }
- if (this.dmg && (this.message == "")) this.message = "was squished";
- if (this.dmg && (this.message2 == "")) this.message2 = "was squished by";
+ if (this.dmg && (this.message == ""))
+ {
+ this.message = "was squished";
+ }
+ if (this.dmg && (this.message2 == ""))
+ {
+ this.message2 = "was squished by";
+ }
if (this.sounds == 1)
{
this.noise1 = "plats/medplat2.wav";
}
+ // WARNING: backwards compatibility because people don't use already existing fields :(
if (this.sound1)
this.noise = this.sound1;
if (this.sound2)
this.noise1 = this.sound2;
- if(this.noise && this.noise != "") { precache_sound(this.noise); }
- if(this.noise1 && this.noise1 != "") { precache_sound(this.noise1); }
+ if(this.noise && this.noise != "")
+ {
+ precache_sound(this.noise);
+ }
+ if(this.noise1 && this.noise1 != "")
+ {
+ precache_sound(this.noise1);
+ }
this.mangle = this.angles;
this.angles = '0 0 0';
#ifdef SVQC
// NOTE: also contains func_sparks
-bool pointparticles_SendEntity(entity this, entity to, float fl)
+bool pointparticles_SendEntity(entity this, entity to, float sendflags)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES);
// optional features to save space
- fl = fl & 0x0F;
- if(this.spawnflags & 2)
- fl |= 0x10; // absolute count on toggle-on
+ sendflags = sendflags & 0x0F;
+ if(this.spawnflags & PARTICLES_IMPULSE)
+ sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on
if(this.movedir != '0 0 0' || this.velocity != '0 0 0')
- fl |= 0x20; // 4 bytes - saves CPU
+ sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU
if(this.waterlevel || this.count != 1)
- fl |= 0x40; // 4 bytes - obscure features almost never used
+ sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used
if(this.mins != '0 0 0' || this.maxs != '0 0 0')
- fl |= 0x80; // 14 bytes - saves lots of space
+ sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space
- WriteByte(MSG_ENTITY, fl);
- if(fl & 2)
+ WriteByte(MSG_ENTITY, sendflags);
+ if(sendflags & SF_TRIGGER_UPDATE)
{
- if(this.state)
+ if(this.active == ACTIVE_ACTIVE)
WriteCoord(MSG_ENTITY, this.impulse);
else
WriteCoord(MSG_ENTITY, 0); // off
}
- if(fl & 4)
+ if(sendflags & SF_TRIGGER_RESET)
{
WriteVector(MSG_ENTITY, this.origin);
}
- if(fl & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
if(this.model != "null")
{
WriteShort(MSG_ENTITY, this.modelindex);
- if(fl & 0x80)
+ if(sendflags & SF_POINTPARTICLES_BOUNDS)
{
WriteVector(MSG_ENTITY, this.mins);
WriteVector(MSG_ENTITY, this.maxs);
else
{
WriteShort(MSG_ENTITY, 0);
- if(fl & 0x80)
+ if(sendflags & SF_POINTPARTICLES_BOUNDS)
{
WriteVector(MSG_ENTITY, this.maxs);
}
}
WriteShort(MSG_ENTITY, this.cnt);
WriteString(MSG_ENTITY, this.mdl);
- if(fl & 0x20)
+ if(sendflags & SF_POINTPARTICLES_MOVING)
{
WriteShort(MSG_ENTITY, compressShortVector(this.velocity));
WriteShort(MSG_ENTITY, compressShortVector(this.movedir));
}
- if(fl & 0x40)
+ if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT)
{
WriteShort(MSG_ENTITY, this.waterlevel * 16.0);
WriteByte(MSG_ENTITY, this.count * 16.0);
return 1;
}
-void pointparticles_use(entity this, entity actor, entity trigger)
-{
- this.state = !this.state;
- this.SendFlags |= 2;
-}
-
void pointparticles_think(entity this)
{
if(this.origin != this.oldorigin)
{
- this.SendFlags |= 4;
+ this.SendFlags |= SF_TRIGGER_RESET;
this.oldorigin = this.origin;
}
this.nextthink = time;
}
-void pointparticles_reset(entity this)
-{
- if(this.spawnflags & 1)
- this.state = 1;
- else
- this.state = 0;
-}
-
spawnfunc(func_pointparticles)
{
if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); }
setsize(this, '0 0 0', this.maxs - this.mins);
}
//if(!this.cnt) this.cnt = _particleeffectnum(this.mdl);
+ this.setactive = generic_netlinked_setactive;
- Net_LinkEntity(this, (this.spawnflags & 4), 0, pointparticles_SendEntity);
+ Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity);
IFTARGETED
{
- this.use = pointparticles_use;
- this.reset = pointparticles_reset;
- this.reset(this);
+ // backwards compatibility
+ this.use = generic_netlinked_legacy_use;
}
- else
- this.state = 1;
+ this.reset = generic_netlinked_reset;
+ this.reset(this);
setthink(this, pointparticles_think);
this.nextthink = time;
}
spawnfunc(func_sparks)
{
- // this.cnt is the amount of sparks that one burst will spawn
- if(this.cnt < 1) {
- this.cnt = 25.0; // nice default value
+ if(this.count < 1) {
+ this.count = 25.0; // nice default value
}
- // this.wait is the probability that a sparkthink will spawn a spark shower
- // range: 0 - 1, but 0 makes little sense, so...
- if(this.wait < 0.05) {
- this.wait = 0.25; // nice default value
+ if(this.impulse < 0.5) {
+ this.impulse = 2.5; // nice default value
}
- this.count = this.cnt;
this.mins = '0 0 0';
this.maxs = '0 0 0';
this.velocity = '0 0 -1';
this.mdl = "TE_SPARK";
- this.impulse = 10 * this.wait; // by default 2.5/sec
- this.wait = 0;
this.cnt = 0; // use mdl
spawnfunc_func_pointparticles(this);
classfield(PointParticles) .string noise; // sound
classfield(PointParticles) .float atten;
classfield(PointParticles) .float volume;
-classfield(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle
+classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle
classfield(PointParticles) .vector movedir; // trace direction
classfield(PointParticles) .float glow_color; // palette index
+const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2;
+
void Draw_PointParticles(entity this)
{
float n, i, fail;
o = this.origin;
sz = this.maxs - this.mins;
n = doBGMScript(this);
- if(this.absolute == 2)
+ if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE)
{
if(n >= 0)
n = this.just_toggled ? this.impulse : 0;
{
float i;
vector v;
- int f = ReadByte();
- if(f & 2)
+ int sendflags = ReadByte();
+ if(sendflags & SF_TRIGGER_UPDATE)
{
i = ReadCoord(); // density (<0: point, >0: volume)
if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed
this.just_toggled = 1;
this.impulse = i;
}
- if(f & 4)
+ if(sendflags & SF_TRIGGER_RESET)
{
this.origin = ReadVector();
}
- if(f & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
this.modelindex = ReadShort();
- if(f & 0x80)
+ if(sendflags & SF_POINTPARTICLES_BOUNDS)
{
if(this.modelindex)
{
this.cnt = ReadShort(); // effect number
this.mdl = strzone(ReadString()); // effect string
- if(f & 0x20)
+ if(sendflags & SF_POINTPARTICLES_MOVING)
{
this.velocity = decompressShortVector(ReadShort());
this.movedir = decompressShortVector(ReadShort());
{
this.velocity = this.movedir = '0 0 0';
}
- if(f & 0x40)
+ if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT)
{
this.waterlevel = ReadShort() / 16.0;
this.count = ReadByte() / 16.0;
return = true;
- if(f & 2)
+ if(sendflags & SF_TRIGGER_UPDATE)
{
this.absolute = (this.impulse >= 0);
if(!this.absolute)
{
v = this.maxs - this.mins;
- this.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube
+ this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube
}
}
- if(f & 0x10)
- this.absolute = 2;
+ if(sendflags & SF_POINTPARTICLES_IMPULSE)
+ this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE;
setorigin(this, this.origin);
setsize(this, this.mins, this.maxs);
#pragma once
+
+// spawnflags
+const int PARTICLES_IMPULSE = BIT(1);
+const int PARTICLES_VISCULLING = BIT(2);
+
+// sendflags
+const int SF_POINTPARTICLES_IMPULSE = BIT(4);
+const int SF_POINTPARTICLES_MOVING = BIT(5); // Send velocity and movedir
+const int SF_POINTPARTICLES_JITTER_AND_COUNT = BIT(6); // Send waterlevel (=jitter) and count
+const int SF_POINTPARTICLES_BOUNDS = BIT(7); // Send min and max of the brush
this.solid = SOLID_NOT;
SetBrushEntityModel(this);
if (!this.cnt)
+ {
this.cnt = 12;
+ }
if (!this.count)
this.count = 2000;
- this.count = 0.01 * this.count * (this.size_x / 1024) * (this.size_y / 1024);
+ // relative to absolute particle count
+ this.count = this.count * (this.size_x / 1024) * (this.size_y / 1024);
if (this.count < 1)
this.count = 1;
if(this.count > 65535)
this.count = 65535;
- this.state = 1; // 1 is rain, 0 is snow
- this.Version = 1;
+ this.state = RAINSNOW_RAIN;
Net_LinkEntity(this, false, 0, rainsnow_SendEntity);
}
this.solid = SOLID_NOT;
SetBrushEntityModel(this);
if (!this.cnt)
+ {
this.cnt = 12;
+ }
if (!this.count)
this.count = 2000;
- this.count = 0.01 * this.count * (this.size_x / 1024) * (this.size_y / 1024);
+ // relative to absolute particle count
+ this.count = this.count * (this.size_x / 1024) * (this.size_y / 1024);
if (this.count < 1)
this.count = 1;
if(this.count > 65535)
this.count = 65535;
- this.state = 0; // 1 is rain, 0 is snow
- this.Version = 1;
+ this.state = RAINSNOW_SNOW;
Net_LinkEntity(this, false, 0, rainsnow_SendEntity);
}
NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew)
{
- this.impulse = ReadByte(); // Rain, Snow, or Whatever
+ this.state = ReadByte(); // Rain, Snow, or Whatever
this.origin = ReadVector();
this.maxs = ReadVector();
this.velocity = decompressShortVector(ReadShort());
- this.count = ReadShort() * 10;
+ this.count = ReadShort();
this.glow_color = ReadByte(); // color
return = true;
setsize(this, this.mins, this.maxs);
this.solid = SOLID_NOT;
if (isnew) IL_PUSH(g_drawables, this);
- if(this.impulse)
+ if(this.state == RAINSNOW_RAIN)
this.draw = Draw_Rain;
else
this.draw = Draw_Snow;
#pragma once
+
+
+const int RAINSNOW_SNOW = 0;
+const int RAINSNOW_RAIN = 1;
#include "rotating.qh"
#ifdef SVQC
-const int FUNC_ROTATING_STARTOFF = BIT(4);
void func_rotating_setactive(entity this, int astate)
{
this.active = astate;
if(this.active == ACTIVE_NOT)
+ {
this.avelocity = '0 0 0';
+ stopsound(this, CH_AMBIENT_SINGLE);
+ }
else
+ {
this.avelocity = this.pos1;
+ if(this.noise && this.noise != "")
+ {
+ _sound(this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE);
+ }
+ }
}
void func_rotating_reset(entity this)
if(this.spawnflags & FUNC_ROTATING_STARTOFF)
{
- this.avelocity = '0 0 0';
- this.active = ACTIVE_NOT;
+ this.setactive(this, ACTIVE_NOT);
}
else
{
- this.avelocity = this.pos1;
- this.active = ACTIVE_ACTIVE;
+ this.setactive(this, ACTIVE_ACTIVE);
+ }
+}
+
+void func_rotating_init_for_player(entity this, entity player)
+{
+ if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player))
+ {
+ msg_entity = player;
+ soundto (MSG_ONE, this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE);
}
}
spawnfunc(func_rotating)
{
- if (this.noise != "")
+ if (this.noise && this.noise != "")
{
precache_sound(this.noise);
- ambientsound(this.origin, this.noise, VOL_BASE, ATTEN_IDLE);
}
this.setactive = func_rotating_setactive;
if (!this.speed)
this.speed = 100;
- // FIXME: test if this turns the right way, then remove this comment (negate as needed)
- if (this.spawnflags & BIT(2)) // X (untested)
+ if (this.spawnflags & FUNC_ROTATING_XAXIS)
this.avelocity = '0 0 1' * this.speed;
- // FIXME: test if this turns the right way, then remove this comment (negate as needed)
- else if (this.spawnflags & BIT(3)) // Y (untested)
+ else if (this.spawnflags & FUNC_ROTATING_YAXIS)
this.avelocity = '1 0 0' * this.speed;
- // FIXME: test if this turns the right way, then remove this comment (negate as needed)
else // Z
this.avelocity = '0 1 0' * this.speed;
this.pos1 = this.avelocity;
- // do this after setting pos1, so we can safely reactivate the func_rotating
- if(this.spawnflags & FUNC_ROTATING_STARTOFF)
- {
- this.avelocity = '0 0 0';
- this.active = ACTIVE_NOT;
- }
- else
- this.active = ACTIVE_ACTIVE;
-
if(this.dmg && (this.message == ""))
this.message = " was squished";
if(this.dmg && (this.message2 == ""))
setthink(this, SUB_NullThink); // for PushMove
this.reset = func_rotating_reset;
+ this.reset(this);
+
+ // maybe send sound to new players
+ IL_PUSH(g_initforplayer, this);
+ this.init_for_player = func_rotating_init_for_player;
}
#endif
#pragma once
+
+
+const int FUNC_ROTATING_XAXIS = BIT(2);
+const int FUNC_ROTATING_YAXIS = BIT(3);
+const int FUNC_ROTATING_STARTOFF = BIT(4);
entity targ, cp;
vector ang;
targ = this.future_target;
- if((this.spawnflags & 1) && targ.curvetarget)
+ if((this.spawnflags & TRAIN_CURVE) && targ.curvetarget)
cp = find(NULL, targetname, targ.curvetarget);
else
cp = NULL;
#ifdef SVQC
entity tg = this.future_target;
- if(tg.spawnflags & 4)
+ if(tg.spawnflags & TRAIN_NEEDACTIVATION)
{
this.use = train_use;
setthink(this, func_null);
this.target_random = targ.target_random;
this.future_target = train_next_find(targ);
- if (this.spawnflags & 1)
+ if (this.spawnflags & TRAIN_CURVE)
{
if(targ.curvetarget)
{
{
this.nextthink = this.ltime + 1;
setthink(this, train_next);
- this.use = func_null; // not again
+ this.use = func_null; // not again, next target can set it again if needed
if(trigger.target2 && trigger.target2 != "")
this.future_target = find(NULL, targetname, trigger.target2);
}
objerror(this, "func_train_find: no next target");
setorigin(this, targ.origin - this.view_ofs);
- if(!(this.spawnflags & 4))
+ if(!(this.spawnflags & TRAIN_NEEDACTIVATION))
{
this.nextthink = this.ltime + 1;
setthink(this, train_next);
return;
this.effects |= EF_LOWPRECISION;
- if(this.spawnflags & 4)
+ if(this.spawnflags & TRAIN_NEEDACTIVATION)
this.use = train_use;
- if (this.spawnflags & 2)
+ if (this.spawnflags & TRAIN_TURN)
{
this.platmovetype_turn = true;
this.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now
#pragma once
+
+const int TRAIN_CURVE = BIT(0);
+const int TRAIN_TURN = BIT(1);
+const int TRAIN_NEEDACTIVATION = BIT(2);
+
#ifdef CSQC
.float dmgtime;
#endif
.float targetfactor, target2factor, target3factor, target4factor;
.vector targetnormal, target2normal, target3normal, target4normal;
-vector func_vectormamamam_origin(entity o, float t)
+vector func_vectormamamam_origin(entity o, float timestep)
{
vector v, p;
- float f;
+ float flags;
entity e;
- f = o.spawnflags;
+ flags = o.spawnflags;
v = '0 0 0';
e = o.wp00;
if(e)
{
- p = e.origin + t * e.velocity;
- if(f & 1)
+ p = e.origin + timestep * e.velocity;
+ if(flags & PROJECT_ON_TARGETNORMAL)
v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor;
else
v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor;
e = o.wp01;
if(e)
{
- p = e.origin + t * e.velocity;
- if(f & 2)
+ p = e.origin + timestep * e.velocity;
+ if(flags & PROJECT_ON_TARGET2NORMAL)
v = v + (p * o.target2normal) * o.target2normal * o.target2factor;
else
v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor;
e = o.wp02;
if(e)
{
- p = e.origin + t * e.velocity;
- if(f & 4)
+ p = e.origin + timestep * e.velocity;
+ if(flags & PROJECT_ON_TARGET3NORMAL)
v = v + (p * o.target3normal) * o.target3normal * o.target3factor;
else
v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor;
e = o.wp03;
if(e)
{
- p = e.origin + t * e.velocity;
- if(f & 8)
+ p = e.origin + timestep * e.velocity;
+ if(flags & PROJECT_ON_TARGET4NORMAL)
v = v + (p * o.target4normal) * o.target4normal * o.target4factor;
else
v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor;
void func_vectormamamam_controller_think(entity this)
{
- this.nextthink = time + 0.1;
+ this.nextthink = time + vectormamamam_timestep;
if(this.owner.active != ACTIVE_ACTIVE)
{
}
if(this.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed
- this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, 0.1) - this.owner.origin) * 10;
+ this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, vectormamamam_timestep) - this.owner.origin) * 10;
}
void func_vectormamamam_findtarget(entity this)
setthink(controller, func_vectormamamam_controller_think);
}
+void func_vectormamamam_setactive(entity this, int astate)
+{
+ if (astate == ACTIVE_TOGGLE)
+ {
+ if(this.active == ACTIVE_ACTIVE)
+ this.active = ACTIVE_NOT;
+ else
+ this.active = ACTIVE_ACTIVE;
+ }
+ else
+ this.active = astate;
+
+ if(this.active == ACTIVE_NOT)
+ {
+ stopsound(this, CH_TRIGGER_SINGLE);
+ }
+ else
+ {
+ if(this.noise && this.noise != "")
+ {
+ _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE);
+ }
+ }
+}
+
+void func_vectormamamam_init_for_player(entity this, entity player)
+{
+ if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player))
+ {
+ msg_entity = player;
+ soundto(MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE);
+ }
+}
+
spawnfunc(func_vectormamamam)
{
if (this.noise != "")
{
precache_sound(this.noise);
- soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE);
}
if(!this.targetfactor)
this.dmgtime = 0.25;
this.dmgtime2 = time;
- if(this.netname == "")
- this.netname = "1 0 0 0 1";
-
if (!InitMovingBrushTrigger(this))
return;
// Savage: Reduce bandwith, critical on e.g. nexdm02
this.effects |= EF_LOWPRECISION;
- this.active = ACTIVE_ACTIVE;
+ this.setactive = func_vectormamamam_setactive;
+ this.setactive(this, ACTIVE_ACTIVE);
+
+ // maybe send sound to new players
+ IL_PUSH(g_initforplayer, this);
+ this.init_for_player = func_vectormamamam_init_for_player;
InitializeEntity(this, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
}
#pragma once
+
+
+const int PROJECT_ON_TARGETNORMAL = BIT(0);
+const int PROJECT_ON_TARGET2NORMAL = BIT(1);
+const int PROJECT_ON_TARGET3NORMAL = BIT(2);
+const int PROJECT_ON_TARGET4NORMAL = BIT(3);
+
+const float vectormamamam_timestep = 0.1;
objerror(this, "follow: could not find target/killtarget");
return;
}
- else if(this.spawnflags & 1)
+ else if(this.spawnflags & FOLLOW_ATTACH)
{
// attach
- if(this.spawnflags & 2)
+ if(this.spawnflags & FOLLOW_LOCAL)
{
setattachment(dst, src, this.message);
}
}
else
{
- if(this.spawnflags & 2)
+ if(this.spawnflags & FOLLOW_LOCAL)
{
set_movetype(dst, MOVETYPE_FOLLOW);
dst.aiment = src;
#pragma once
+
+
+const int FOLLOW_ATTACH = BIT(0);
+const int FOLLOW_LOCAL = BIT(1);
vector a;
if(this.enemy)
{
- if(this.spawnflags & 2)
+ if(this.spawnflags & LASER_FINITE)
{
if(this.enemy.origin != this.mangle)
{
this.mangle = this.enemy.origin;
- this.SendFlags |= 2;
+ this.SendFlags |= SF_LASER_UPDATE_TARGET;
}
}
else
if(a != this.mangle)
{
this.mangle = a;
- this.SendFlags |= 2;
+ this.SendFlags |= SF_LASER_UPDATE_TARGET;
}
}
}
if(this.angles != this.mangle)
{
this.mangle = this.angles;
- this.SendFlags |= 2;
+ this.SendFlags |= SF_LASER_UPDATE_TARGET;
}
}
if(this.origin != this.oldorigin)
{
- this.SendFlags |= 1;
+ this.SendFlags |= SF_LASER_UPDATE_ORIGIN;
this.oldorigin = this.origin;
}
}
this.nextthink = time;
- if(!this.state)
+ if(this.active == ACTIVE_NOT)
return;
misc_laser_aim(this);
if(this.enemy)
{
o = this.enemy.origin;
- if (!(this.spawnflags & 2))
- o = this.origin + normalize(o - this.origin) * 32768;
+ if (!(this.spawnflags & LASER_FINITE))
+ o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH;
}
else
{
makevectors(this.mangle);
- o = this.origin + v_forward * 32768;
+ o = this.origin + v_forward * LASER_BEAM_MAXLENGTH;
}
if(this.dmg || this.enemy.target != "")
if(this.dmg)
{
if(this.team)
- if(((this.spawnflags & 8) == 0) == (this.team != hitent.team))
+ if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team))
return;
if(hitent.takedamage)
Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0');
}
}
-bool laser_SendEntity(entity this, entity to, float fl)
+bool laser_SendEntity(entity this, entity to, float sendflags)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER);
- fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser
- if(this.spawnflags & 2)
- fl |= 0x80;
+ sendflags = sendflags & 0x0F; // use that bit to indicate finite length laser
+ if(this.spawnflags & LASER_FINITE)
+ sendflags |= SF_LASER_FINITE;
if(this.alpha)
- fl |= 0x40;
+ sendflags |= SF_LASER_ALPHA;
if(this.scale != 1 || this.modelscale != 1)
- fl |= 0x20;
- if(this.spawnflags & 4)
- fl |= 0x10;
- WriteByte(MSG_ENTITY, fl);
- if(fl & 1)
+ sendflags |= SF_LASER_SCALE;
+ if(this.spawnflags & LASER_NOTRACE)
+ sendflags |= SF_LASER_NOTRACE;
+ WriteByte(MSG_ENTITY, sendflags);
+ if(sendflags & SF_LASER_UPDATE_ORIGIN)
{
WriteVector(MSG_ENTITY, this.origin);
}
- if(fl & 8)
+ if(sendflags & SF_LASER_UPDATE_EFFECT)
{
- WriteByte(MSG_ENTITY, this.colormod_x * 255.0);
- WriteByte(MSG_ENTITY, this.colormod_y * 255.0);
- WriteByte(MSG_ENTITY, this.colormod_z * 255.0);
- if(fl & 0x40)
+ WriteByte(MSG_ENTITY, this.beam_color.x * 255.0);
+ WriteByte(MSG_ENTITY, this.beam_color.y * 255.0);
+ WriteByte(MSG_ENTITY, this.beam_color.z * 255.0);
+ if(sendflags & SF_LASER_ALPHA)
WriteByte(MSG_ENTITY, this.alpha * 255.0);
- if(fl & 0x20)
+ if(sendflags & SF_LASER_SCALE)
{
WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255));
WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255));
}
- if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
- WriteShort(MSG_ENTITY, this.cnt + 1);
+ if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) // effect doesn't need sending if the laser is infinite and has collision testing turned off
+ WriteShort(MSG_ENTITY, this.cnt);
}
- if(fl & 2)
+ if(sendflags & SF_LASER_UPDATE_TARGET)
{
- if(fl & 0x80)
+ if(sendflags & SF_LASER_FINITE)
{
WriteVector(MSG_ENTITY, this.enemy.origin);
}
WriteAngle(MSG_ENTITY, this.mangle_y);
}
}
- if(fl & 4)
- WriteByte(MSG_ENTITY, this.state);
- return 1;
+ if(sendflags & SF_LASER_UPDATE_ACTIVE)
+ WriteByte(MSG_ENTITY, this.active);
+ return true;
}
/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED
spawnfunc_target_position where the laser ends
"mdl"
name of beam end effect to use
-"colormod"
+"beam_color"
color of the beam (default: red)
"dmg"
damage per second (-1 for a laser that kills immediately)
*/
-void laser_use(entity this, entity actor, entity trigger)
+
+void laser_setactive(entity this, int act)
{
- this.state = !this.state;
- this.SendFlags |= 4;
- misc_laser_aim(this);
+ int old_status = this.active;
+ if(act == ACTIVE_TOGGLE)
+ {
+ if(this.active == ACTIVE_ACTIVE)
+ {
+ this.active = ACTIVE_NOT;
+ }
+ else
+ {
+ this.active = ACTIVE_ACTIVE;
+ }
+ }
+ else
+ {
+ this.active = act;
+ }
+
+ if (this.active != old_status)
+ {
+ this.SendFlags |= SF_LASER_UPDATE_ACTIVE;
+ misc_laser_aim(this);
+ }
}
-void laser_reset(entity this)
+void laser_use(entity this, entity actor, entity trigger)
{
- if(this.spawnflags & 1)
- this.state = 1;
- else
- this.state = 0;
+ this.setactive(this, ACTIVE_TOGGLE);
}
spawnfunc(misc_laser)
if(this.cnt < 0)
this.cnt = -1;
- if(this.colormod == '0 0 0')
+ if(!this.beam_color && this.colormod)
+ {
+ LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead");
+ this.beam_color = this.colormod;
+ }
+
+ if(this.beam_color == '0 0 0')
+ {
if(!this.alpha)
- this.colormod = '1 0 0';
- if(this.message == "") this.message = "saw the light";
- if (this.message2 == "") this.message2 = "was pushed into a laser by";
- if(!this.scale) this.scale = 1;
- if(!this.modelscale) this.modelscale = 1;
- else if(this.modelscale < 0) this.modelscale = 0;
+ this.beam_color = '1 0 0';
+ }
+
+ if(this.message == "")
+ {
+ this.message = "saw the light";
+ }
+ if (this.message2 == "")
+ {
+ this.message2 = "was pushed into a laser by";
+ }
+ if(!this.scale)
+ {
+ this.scale = 1;
+ }
+ if(!this.modelscale)
+ {
+ this.modelscale = 1;
+ }
+ else if(this.modelscale < 0)
+ {
+ this.modelscale = 0;
+ }
setthink(this, misc_laser_think);
this.nextthink = time;
InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET);
Net_LinkEntity(this, false, 0, laser_SendEntity);
+ this.setactive = laser_setactive;
+
IFTARGETED
{
- this.reset = laser_reset;
- this.reset(this);
+ // backwards compatibility
this.use = laser_use;
}
- else
- this.state = 1;
+
+ this.reset = generic_netlinked_reset;
+ this.reset(this);
}
#elif defined(CSQC)
// a laser goes from origin in direction angles
-// it has color 'colormod'
+// it has color 'beam_color'
// and stops when something is in the way
entityclass(Laser);
classfield(Laser) .int cnt; // end effect
classfield(Laser) .vector colormod;
classfield(Laser) .int state; // on-off
classfield(Laser) .int count; // flags for the laser
-classfield(Laser) .vector velocity;
+classfield(Laser) .vector velocity; // laser endpoint if it is FINITE
classfield(Laser) .float alpha;
classfield(Laser) .float scale; // scaling factor of the thickness
classfield(Laser) .float modelscale; // scaling factor of the dlight
void Draw_Laser(entity this)
{
- if(!this.state)
+ if(this.active == ACTIVE_NOT)
return;
InterpolateOrigin_Do(this);
- if(this.count & 0x80)
+ if(this.count & SF_LASER_FINITE)
{
- if(this.count & 0x10)
+ if(this.count & SF_LASER_NOTRACE)
{
trace_endpos = this.velocity;
trace_dphitq3surfaceflags = 0;
}
else
{
- if(this.count & 0x10)
+ if(this.count & SF_LASER_NOTRACE)
{
makevectors(this.angles);
- trace_endpos = this.origin + v_forward * 1048576;
+ trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY;
}
else
{
makevectors(this.angles);
- traceline(this.origin, this.origin + v_forward * 32768, 0, this);
+ traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this);
if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
- trace_endpos = this.origin + v_forward * 1048576;
+ trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE;
}
}
if(this.scale != 0)
{
if(this.alpha)
{
- Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.colormod, this.alpha, DRAWFLAG_NORMAL, view_origin);
+ Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin);
}
else
{
- Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin);
+ Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin);
}
}
if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
{
if(this.cnt >= 0)
__pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000);
- if(this.colormod != '0 0 0' && this.modelscale != 0)
- adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.colormod * 5);
+ if(this.beam_color != '0 0 0' && this.modelscale != 0)
+ adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5);
}
}
InterpolateOrigin_Undo(this);
// 30 bytes, or 13 bytes for just moving
- int f = ReadByte();
- this.count = (f & 0xF0);
+ int sendflags = ReadByte();
+ this.count = (sendflags & 0xF0);
- if(this.count & 0x80)
+ if(this.count & SF_LASER_FINITE)
this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
else
this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN;
- if(f & 1)
+ if(sendflags & SF_LASER_UPDATE_ORIGIN)
{
this.origin = ReadVector();
setorigin(this, this.origin);
}
- if(f & 8)
+ if(sendflags & SF_LASER_UPDATE_EFFECT)
{
- this.colormod_x = ReadByte() / 255.0;
- this.colormod_y = ReadByte() / 255.0;
- this.colormod_z = ReadByte() / 255.0;
- if(f & 0x40)
+ this.beam_color.x = ReadByte() / 255.0;
+ this.beam_color.y = ReadByte() / 255.0;
+ this.beam_color.z = ReadByte() / 255.0;
+ if(sendflags & SF_LASER_ALPHA)
this.alpha = ReadByte() / 255.0;
else
this.alpha = 0;
- this.scale = 2;
- this.modelscale = 50;
- if(f & 0x20)
+ this.scale = 2; // NOTE: why 2?
+ this.modelscale = 50; // NOTE: why 50?
+ if(sendflags & SF_LASER_SCALE)
{
this.scale *= ReadByte() / 16.0; // beam radius
this.modelscale *= ReadByte() / 16.0; // dlight radius
}
- if((f & 0x80) || !(f & 0x10))
- this.cnt = ReadShort() - 1; // effect number
+ if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE))
+ this.cnt = ReadShort(); // effect number
else
this.cnt = 0;
}
- if(f & 2)
+ if(sendflags & SF_LASER_UPDATE_TARGET)
{
- if(f & 0x80)
+ if(sendflags & SF_LASER_FINITE)
{
this.velocity = ReadVector();
}
this.angles_y = ReadAngle();
}
}
- if(f & 4)
- this.state = ReadByte();
+ if(sendflags & SF_LASER_UPDATE_ACTIVE)
+ this.active = ReadByte();
return = true;
#pragma once
+
+
+const int LASER_FINITE = BIT(1);
+const int LASER_NOTRACE = BIT(2);
+const int LASER_INVERT_TEAM = BIT(3);
+
+const int SF_LASER_UPDATE_ORIGIN = BIT(0);
+const int SF_LASER_UPDATE_TARGET = BIT(1);
+const int SF_LASER_UPDATE_ACTIVE = BIT(2);
+const int SF_LASER_UPDATE_EFFECT = BIT(3);
+
+const int SF_LASER_NOTRACE = BIT(4);
+const int SF_LASER_SCALE = BIT(5);
+const int SF_LASER_ALPHA = BIT(6);
+const int SF_LASER_FINITE = BIT(7);
+
+.vector beam_color;
+
+const float LASER_BEAM_MAXLENGTH = 32768; // maximum length of a beam trace
+// TODO: find a better way to do this
+const float LASER_BEAM_MAXWORLDSIZE = 1048576; // to make sure the endpoint of the beam is not visible inside
#ifdef SVQC
-bool teleport_dest_send(entity this, entity to, int sf)
+bool teleport_dest_send(entity this, entity to, int sendflags)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_TELEPORT_DEST);
- WriteByte(MSG_ENTITY, sf);
+ WriteByte(MSG_ENTITY, sendflags);
- if(sf & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
WriteByte(MSG_ENTITY, this.cnt);
WriteCoord(MSG_ENTITY, this.speed);
void teleport_dest_link(entity this)
{
Net_LinkEntity(this, false, 0, teleport_dest_send);
- this.SendFlags |= 1; // update
+ this.SendFlags |= SF_TRIGGER_INIT;
}
spawnfunc(info_teleport_destination)
NET_HANDLE(ENT_CLIENT_TELEPORT_DEST, bool isnew)
{
- int sf = ReadByte();
+ int sendflags = ReadByte();
- if(sf & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
this.classname = "info_teleport_destination";
this.cnt = ReadByte();
void plat_hit_top(entity this)
{
_sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
- this.state = 1;
+ this.state = STATE_TOP;
setthink(this, plat_go_down);
this.nextthink = this.ltime + 3;
void plat_hit_bottom(entity this)
{
_sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
- this.state = 2;
+ this.state = STATE_BOTTOM;
}
void plat_go_down(entity this)
{
_sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM);
- this.state = 3;
+ this.state = STATE_DOWN;
SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom);
}
void plat_go_up(entity this)
{
_sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM);
- this.state = 4;
+ this.state = STATE_UP;
SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top);
}
return;
#endif
- if (this.enemy.state == 2) {
+ if (this.enemy.state == STATE_BOTTOM) {
plat_go_up(this.enemy);
- } else if (this.enemy.state == 1)
+ } else if (this.enemy.state == STATE_TOP)
this.enemy.nextthink = this.enemy.ltime + 1;
}
return;
#endif
- if (this.enemy.state == 1) {
+ if (this.enemy.state == STATE_TOP) {
entity e = this.enemy;
plat_go_down(e);
}
void plat_crush(entity this, entity blocker)
{
- if((this.spawnflags & 4) && (blocker.takedamage != DAMAGE_NO))
+ if((this.spawnflags & CRUSH) && (blocker.takedamage != DAMAGE_NO))
{ // KIll Kill Kill!!
#ifdef SVQC
Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0');
}
#endif
- if (this.state == 4)
+ if (this.state == STATE_UP)
plat_go_down (this);
- else if (this.state == 3)
+ else if (this.state == STATE_DOWN)
plat_go_up (this);
// when in other states, then the plat_crush event came delayed after
// plat state already had changed
void plat_use(entity this, entity actor, entity trigger)
{
this.use = func_null;
- if (this.state != 4)
+ if (this.state != STATE_UP)
objerror (this, "plat_use: not in up state");
plat_go_down(this);
}
+// WARNING: backwards compatibility because people don't use already existing fields :(
+// TODO: Check if any maps use these fields and remove these fields if it doesn't break maps
.string sound1, sound2;
void plat_reset(entity this)
IFTARGETED
{
setorigin(this, this.pos1);
- this.state = 4;
+ this.state = STATE_UP;
this.use = plat_use;
}
else
{
setorigin(this, this.pos2);
- this.state = 2;
+ this.state = STATE_BOTTOM;
this.use = plat_trigger_use;
}
#pragma once
+
+const int PLAT_LOW_TRIGGER = BIT(0);
+
.float dmgtime2;
void plat_center_touch(entity this, entity toucher);
void plat_go_up(entity this);
void plat_go_down(entity this);
void plat_crush(entity this, entity blocker);
-const float PLAT_LOW_TRIGGER = 1;
.float dmg;
// derivative: delta + 2 * delta2 (e.g. for angle positioning)
entity own = this.owner;
setthink(own, this.think1);
+ // set the owner's reference to this entity to NULL
+ own.move_controller = NULL;
delete(this);
getthink(own)(own);
}
return;
}
+ // delete the previous controller, otherwise changing movement midway is glitchy
+ if (this.move_controller != NULL)
+ {
+ delete(this.move_controller);
+ }
controller = new(SUB_CalcMove_controller);
controller.owner = this;
+ this.move_controller = controller;
controller.platmovetype = this.platmovetype;
controller.platmovetype_start = this.platmovetype_start;
controller.platmovetype_end = this.platmovetype_end;
#pragma once
+#include "defs.qh"
void SUB_SetFade (entity ent, float when, float fading_time);
void SUB_VanishOrRemove (entity ent);
.vector dest1, dest2;
+.entity move_controller;
+
#ifdef CSQC
// this stuff is defined in the server side engine VM, so we must define it separately here
.float takedamage;
-const float DAMAGE_NO = 0;
-const float DAMAGE_YES = 1;
-const float DAMAGE_AIM = 2;
-
-float STATE_TOP = 0;
-float STATE_BOTTOM = 1;
-float STATE_UP = 2;
-float STATE_DOWN = 3;
+const int DAMAGE_NO = 0;
+const int DAMAGE_YES = 1;
+const int DAMAGE_AIM = 2;
.string noise, noise1, noise2, noise3; // contains names of wavs to play
void target_changelevel_use(entity this, entity actor, entity trigger)
{
- if(this.spawnflags & 2)
+ if(this.spawnflags & CHANGELEVEL_MULTIPLAYER)
{
// simply don't react if a non-player triggers it
if(!IS_PLAYER(actor)) { return; }
localcmd(strcat("changelevel ", this.chmap, "\n"));
}
+/*target_changelevel
+Target to change/end level
+KEYS:
+chmap: map to switch to, leave empty for endmatch
+gametype: gametype for the next map
+count: fraction of real players that need to trigger this entity for levelchange
+SPAWNFLAGS:
+CHANGELEVEL_MULTIPLAYER: multiplayer support
+*/
+
spawnfunc(target_changelevel)
{
this.use = target_changelevel_use;
- if(!this.count) { this.count = 0.7; }
+ if(!this.count)
+ {
+ this.count = 0.7;
+ }
}
#endif
#pragma once
+
+
+const int CHANGELEVEL_MULTIPLAYER = BIT(1);
spawnfunc(target_levelwarp)
{
+ // this.cnt is index (starting from 1) of the campaign level to warp to
+ // 0 means next level
this.use = target_levelwarp_use;
}
#endif
#ifdef SVQC
IntrusiveList g_targetmusic_list;
-STATIC_INIT(g_targetmusic_list) { g_targetmusic_list = IL_NEW(); }
+STATIC_INIT(g_targetmusic_list)
+{
+ g_targetmusic_list = IL_NEW();
+}
// values:
// volume
}
void target_music_reset(entity this)
{
- if (this.targetname == "") target_music_sendto(this, MSG_ALL, 1);
+ if (this.targetname == "")
+ {
+ target_music_sendto(this, MSG_ALL, true);
+ }
}
void target_music_kill()
{
{
it.volume = 0;
if (it.targetname == "")
- target_music_sendto(it, MSG_ALL, 1);
+ target_music_sendto(it, MSG_ALL, true);
else
- target_music_sendto(it, MSG_ALL, 0);
+ target_music_sendto(it, MSG_ALL, false);
});
}
void target_music_use(entity this, entity actor, entity trigger)
if(IS_REAL_CLIENT(actor))
{
msg_entity = actor;
- target_music_sendto(this, MSG_ONE, 1);
+ target_music_sendto(this, MSG_ONE, true);
}
FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, {
msg_entity = it;
- target_music_sendto(this, MSG_ONE, 1);
+ target_music_sendto(this, MSG_ONE, true);
});
}
spawnfunc(target_music)
this.volume = 1;
IL_PUSH(g_targetmusic_list, this);
if(this.targetname == "")
- target_music_sendto(this, MSG_INIT, 1);
+ target_music_sendto(this, MSG_INIT, true);
else
- target_music_sendto(this, MSG_INIT, 0);
+ target_music_sendto(this, MSG_INIT, false);
}
void TargetMusic_RestoreGame()
{
IL_EACH(g_targetmusic_list, true,
{
if(it.targetname == "")
- target_music_sendto(it, MSG_INIT, 1);
+ target_music_sendto(it, MSG_INIT, true);
else
- target_music_sendto(it, MSG_INIT, 0);
+ target_music_sendto(it, MSG_INIT, false);
});
}
// values:
// targetname
// fade_time
// spawnflags:
-// 1 = START_OFF
-// when triggered, it is disabled/enabled for everyone
-bool trigger_music_SendEntity(entity this, entity to, float sf)
+// START_DISABLED
+// can be disabled/enabled for everyone with relays
+bool trigger_music_SendEntity(entity this, entity to, int sendflags)
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
- sf &= ~0x80;
- if(this.cnt)
- sf |= 0x80;
- WriteByte(MSG_ENTITY, sf);
- if(sf & 4)
+ WriteByte(MSG_ENTITY, sendflags);
+ if(sendflags & SF_MUSIC_ORIGIN)
{
WriteVector(MSG_ENTITY, this.origin);
}
- if(sf & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
if(this.model != "null")
{
WriteByte(MSG_ENTITY, this.fade_rate * 16.0);
WriteString(MSG_ENTITY, this.noise);
}
- return 1;
+ if(sendflags & SF_TRIGGER_UPDATE)
+ {
+ WriteByte(MSG_ENTITY, this.active);
+ }
+ return true;
}
void trigger_music_reset(entity this)
{
- this.cnt = !(this.spawnflags & 1);
- this.SendFlags |= 0x80;
-}
-void trigger_music_use(entity this, entity actor, entity trigger)
-{
- this.cnt = !this.cnt;
- this.SendFlags |= 0x80;
+ if(this.spawnflags & START_DISABLED)
+ {
+ this.setactive(this, ACTIVE_NOT);
+ }
+ else
+ {
+ this.setactive(this, ACTIVE_ACTIVE);
+ }
}
+
spawnfunc(trigger_music)
{
- if(this.model != "") _setmodel(this, this.model);
- if(!this.volume) this.volume = 1;
+ if(this.model != "")
+ {
+ _setmodel(this, this.model);
+ }
+ if(!this.volume)
+ {
+ this.volume = 1;
+ }
if(!this.modelindex)
{
setorigin(this, this.origin + this.mins);
setsize(this, '0 0 0', this.maxs - this.mins);
}
- trigger_music_reset(this);
- this.use = trigger_music_use;
+ this.setactive = generic_netlinked_setactive;
+ this.use = generic_netlinked_legacy_use; // backwards compatibility
this.reset = trigger_music_reset;
+ this.reset(this);
Net_LinkEntity(this, false, 0, trigger_music_SendEntity);
}
{
// run AFTER all the thinks!
entity best = music_default;
- if (music_target && time < music_target.lifetime) best = music_target;
- if (music_trigger) best = music_trigger;
+ if (music_target && time < music_target.lifetime)
+ {
+ best = music_target;
+ }
+ if (music_trigger)
+ {
+ best = music_trigger;
+ }
LL_EACH(TargetMusic_list, it.noise, {
const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1;
if (it == best)
if (vol != vol0)
{
if(vol0 < 0)
- _sound(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE); // restart
+ sound7(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE, 0, BIT(4)); // restart
else
- _sound(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE);
+ sound7(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE, 0, BIT(4));
it.lastvol = vol;
}
});
void Ent_TriggerMusic_Think(entity this)
{
- if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, this, NULL))
+ if(this.active == ACTIVE_NOT)
+ {
+ return;
+ }
+ vector org = (csqcplayer) ? csqcplayer.origin : view_origin;
+ if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL))
{
music_trigger = this;
}
- this.nextthink = time;
}
void Ent_TriggerMusic_Remove(entity this)
NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew)
{
- int f = ReadByte();
- if(f & 4)
+ int sendflags = ReadByte();
+ if(sendflags & SF_MUSIC_ORIGIN)
{
this.origin = ReadVector();
}
- if(f & 1)
+ if(sendflags & SF_TRIGGER_INIT)
{
this.modelindex = ReadShort();
if(this.modelindex)
if(this.noise != s)
{
precache_sound(this.noise);
- _sound(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE);
+ sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4));
if(getsoundtime(this, CH_BGM_SINGLE) < 0)
{
- LOG_TRACEF("Cannot initialize sound %s", this.noise);
+ LOG_WARNF("Cannot initialize sound %s", this.noise);
strfree(this.noise);
}
}
}
+ if(sendflags & SF_TRIGGER_UPDATE)
+ {
+ this.active = ReadByte();
+ }
setorigin(this, this.origin);
setsize(this, this.mins, this.maxs);
- this.cnt = 1;
- setthink(this, Ent_TriggerMusic_Think);
- this.nextthink = time;
+ this.draw = Ent_TriggerMusic_Think;
+ if(isnew)
+ {
+ LL_PUSH(TargetMusic_list, this);
+ IL_PUSH(g_drawables, this);
+ }
return true;
}
.float lifetime;
+const int SF_MUSIC_ORIGIN = BIT(2);
+
#ifdef CSQC
float music_disabled;
entity music_default;
// "classname" "target_spawn"
// "message" "fieldname value fieldname value ..."
// "spawnflags"
-// 1 = call the spawn function
-// 2 = trigger on map load
+// ON_MAPLOAD = trigger on map load
float target_spawn_initialized;
.void(entity this) target_spawn_spawnfunc;
void target_spawn_spawnfirst(entity this)
{
entity act = this.target_spawn_activator;
- if(this.spawnflags & 2)
+ if(this.spawnflags & ON_MAPLOAD)
target_spawn_use(this, act, NULL);
}
else
snd = this.noise;
_sound(this, CH_TRIGGER_SINGLE, snd, VOL_BASE * this.volume, this.atten);
- if(this.spawnflags & 3)
+ if(this.spawnflags & (SPEAKER_LOOPED_ON + SPEAKER_LOOPED_OFF))
this.use = target_speaker_use_off;
}
void target_speaker_use_off(entity this, entity actor, entity trigger)
}
void target_speaker_reset(entity this)
{
- if(this.spawnflags & 1) // LOOPED_ON
+ if(this.spawnflags & SPEAKER_LOOPED_ON)
{
if(this.use == target_speaker_use_on)
target_speaker_use_on(this, NULL, NULL);
}
- else if(this.spawnflags & 2)
+ else if(this.spawnflags & SPEAKER_LOOPED_OFF)
{
if(this.use == target_speaker_use_off)
target_speaker_use_off(this, NULL, NULL);
if(this.noise)
precache_sound (this.noise);
- if(!this.atten && !(this.spawnflags & 4))
+ if(!this.atten && (this.spawnflags & SPEAKER_GLOBAL))
+ {
+ LOG_WARN("target_speaker uses legacy spawnflag GLOBAL (BIT(2)), please set atten to -1 instead");
+ this.atten = -1;
+ }
+
+ if(!this.atten)
{
IFTARGETED
this.atten = ATTEN_NORM;
IFTARGETED
{
- if(this.spawnflags & 8) // ACTIVATOR
+ if(this.spawnflags & SPEAKER_ACTIVATOR)
this.use = target_speaker_use_activator;
- else if(this.spawnflags & 1) // LOOPED_ON
+ else if(this.spawnflags & SPEAKER_LOOPED_ON)
{
target_speaker_use_on(this, NULL, NULL);
this.reset = target_speaker_reset;
}
- else if(this.spawnflags & 2) // LOOPED_OFF
+ else if(this.spawnflags & SPEAKER_LOOPED_OFF)
{
this.use = target_speaker_use_on;
this.reset = target_speaker_reset;
else
this.use = target_speaker_use_on;
}
- else if(this.spawnflags & 1) // LOOPED_ON
+ else if(this.spawnflags & SPEAKER_LOOPED_ON)
{
ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten);
delete(this);
}
- else if(this.spawnflags & 2) // LOOPED_OFF
+ else if(this.spawnflags & SPEAKER_LOOPED_OFF)
{
objerror(this, "This sound entity can never be activated");
}
#pragma once
+
+
+const int SPEAKER_LOOPED_ON = BIT(0);
+const int SPEAKER_LOOPED_OFF = BIT(1);
+const int SPEAKER_GLOBAL = BIT(2); // legacy, set speaker atten to -1 instead
+const int SPEAKER_ACTIVATOR = BIT(3);
#pragma once
+#include "defs.qh"
IntrusiveList g_teleporters;
STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); }
.entity pusher;
-const float TELEPORT_FLAG_SOUND = 1;
-const float TELEPORT_FLAG_PARTICLES = 2;
-const float TELEPORT_FLAG_TDEATH = 4;
-const float TELEPORT_FLAG_FORCE_TDEATH = 8;
+
+const int TELEPORT_FLAG_SOUND = BIT(0);
+const int TELEPORT_FLAG_PARTICLES = BIT(1);
+const int TELEPORT_FLAG_TDEATH = BIT(2);
+const int TELEPORT_FLAG_FORCE_TDEATH = BIT(3);
#define TELEPORT_FLAGS_WARPZONE 0
#define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH)
#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH)
// types for .teleportable entity setting
-const float TELEPORT_NORMAL = 1; // play sounds/effects etc
-const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special
+const int TELEPORT_NORMAL = 1; // play sounds/effects etc
+const int TELEPORT_SIMPLE = 2; // only do teleport, nothing special
entity Simple_TeleportPlayer(entity teleporter, entity player);
if (this.count < 0)
return;
- bool doactivate = (this.spawnflags & 4);
+ bool doactivate = (this.spawnflags & COUNTER_FIRE_AT_COUNT);
if (this.count == 0)
{
this.count = this.cnt;
}
-/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage
+/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage COUNTER_FIRE_AT_COUNT
Acts as an intermediary for an action that takes multiple inputs.
If nomessage is not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished.
+If COUNTER_FIRE_AT_COUNT is set, it will also fire all of its targets at countdown, making it behave like trigger_mulitple with limited shots
If respawntime is set, it will re-enable itself after the time once the sequence has been completed
-After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
+After the counter has been triggered "count" times (default 2), it will fire all of its targets.
*/
spawnfunc(trigger_counter)
{
#pragma once
+
+
+const int COUNTER_FIRE_AT_COUNT = BIT(2);
spawnfunc(trigger_flipflop)
{
- if(this.spawnflags & 1)
- this.state = 1;
+ if(this.spawnflags & START_ENABLED)
+ {
+ this.state = true;
+ }
this.use = flipflop_use;
this.reset = spawnfunc_trigger_flipflop; // perfect resetter
}
}
}
+// legacy
void trigger_gravity_use(entity this, entity actor, entity trigger)
{
- this.state = !this.state;
+ this.setactive(this, ACTIVE_TOGGLE);
}
void trigger_gravity_touch(entity this, entity toucher)
{
float g;
- if(this.state != true)
+ if(this.active == ACTIVE_NOT)
return;
EXACTTRIGGER_TOUCH(this, toucher);
g = this.gravity;
- if (!(this.spawnflags & 1))
+ if (!(this.spawnflags & GRAVITY_STICKY))
{
if(toucher.trigger_gravity_check)
{
if(this == toucher.trigger_gravity_check.enemy)
{
// same?
+ // NOTE: see explanation in trigger_gravity_check_think
toucher.trigger_gravity_check.count = 2; // gravity one more frame...
return;
}
if(this.noise != "")
precache_sound(this.noise);
- this.state = true;
+ this.active = ACTIVE_ACTIVE;
+ this.setactive = generic_setactive;
IFTARGETED
{
+ // legacy use
this.use = trigger_gravity_use;
- if(this.spawnflags & 2)
- this.state = false;
+ if(this.spawnflags & GRAVITY_START_DISABLED)
+ this.active = ACTIVE_NOT;
}
}
#endif
#pragma once
+
+
+const int GRAVITY_STICKY = BIT(0); // keep gravity multiplier even after exiting the trigger_gravity
+const int GRAVITY_START_DISABLED = BIT(1);
// only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
if (toucher.iscreature)
{
- if (toucher.takedamage)
- if (!IS_DEAD(toucher))
- if (toucher.triggerhealtime < time)
+ if (toucher.takedamage && !IS_DEAD(toucher) && toucher.triggerhealtime < time)
{
bool is_trigger = this.targetname == "";
if(is_trigger)
if(this.delay > 0)
toucher.triggerhealtime = time + this.delay;
- bool playthesound = (this.spawnflags & 4);
+ bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS);
if (toucher.health < this.max_health)
{
playthesound = true;
#pragma once
+
+
+const int HEAL_SOUND_ALWAYS = BIT(2);
return;
if(this.team)
- if(((this.spawnflags & 4) == 0) == (this.team != toucher.team))
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team))
return;
// only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu)
/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ?
Any object touching this will be hurt
set dmg to damage amount
-defalt dmg = 5
+default dmg = 1000
*/
.entity trigger_hurt_next;
entity trigger_hurt_last;
trigger_hurt_last = this;
}
-float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end)
+bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end)
{
entity th;
for(th = trigger_hurt_first; th; th = th.trigger_hurt_next)
- if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax))
+ if(tracebox_hits_box(start, e_min, e_max, end, th.absmin, th.absmax))
return true;
return false;
#include "impulse.qh"
// targeted (directional) mode
-void trigger_impulse_touch1(entity this, entity toucher)
+void trigger_impulse_touch_directional(entity this, entity toucher)
{
entity targ;
float pushdeltatime;
return;
}
- str = min(this.radius, vlen(this.origin - toucher.origin));
-
- if(this.falloff == 1)
- str = (str / this.radius) * this.strength;
- else if(this.falloff == 2)
- str = (1 - (str / this.radius)) * this.strength;
- else
- str = this.strength;
+ // falloff is not supported because radius is always 0 in directional mode
+ str = this.strength;
pushdeltatime = time - toucher.lastpushtime;
- if (pushdeltatime > 0.15) pushdeltatime = 0;
+ if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME)
+ {
+ pushdeltatime = 0;
+ }
toucher.lastpushtime = time;
- if(!pushdeltatime) return;
+ if(!pushdeltatime)
+ {
+ return;
+ }
- if(this.spawnflags & 64)
+ if(this.spawnflags & IMPULSE_DIRECTIONAL_SPEEDTARGET)
{
float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin);
if (addspeed > 0)
{
- float accelspeed = min(8 * pushdeltatime * str, addspeed);
+ float accelspeed = min(IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR * pushdeltatime * str, addspeed);
toucher.velocity += accelspeed * normalize(targ.origin - this.origin);
}
}
}
// Directionless (accelerator/decelerator) mode
-void trigger_impulse_touch2(entity this, entity toucher)
+void trigger_impulse_touch_accel(entity this, entity toucher)
{
float pushdeltatime;
EXACTTRIGGER_TOUCH(this, toucher);
pushdeltatime = time - toucher.lastpushtime;
- if (pushdeltatime > 0.15) pushdeltatime = 0;
+ if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME)
+ {
+ pushdeltatime = 0;
+ }
toucher.lastpushtime = time;
- if(!pushdeltatime) return;
+ if(!pushdeltatime)
+ {
+ return;
+ }
// div0: ticrate independent, 1 = identity (not 20)
toucher.velocity = toucher.velocity * (this.strength ** pushdeltatime);
}
// Spherical (gravity/repulsor) mode
-void trigger_impulse_touch3(entity this, entity toucher)
+void trigger_impulse_touch_radial(entity this, entity toucher)
{
float pushdeltatime;
float str;
EXACTTRIGGER_TOUCH(this, toucher);
pushdeltatime = time - toucher.lastpushtime;
- if (pushdeltatime > 0.15) pushdeltatime = 0;
+ if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME)
+ {
+ pushdeltatime = 0;
+ }
toucher.lastpushtime = time;
- if(!pushdeltatime) return;
+ if(!pushdeltatime)
+ {
+ return;
+ }
setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius);
str = min(this.radius, vlen(this.origin - toucher.origin));
- if(this.falloff == 1)
+ if(this.falloff == FALLOFF_LINEAR)
str = (1 - str / this.radius) * this.strength; // 1 in the inside
- else if(this.falloff == 2)
+ else if(this.falloff == FALLOFF_LINEAR_INV)
str = (str / this.radius) * this.strength; // 0 in the inside
else
str = this.strength;
REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE)
/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ?
+Force field
-------- KEYS --------
target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed.
If not, this trigger acts like a damper/accelerator field.
-strength : This is how mutch force to add in the direction of .target each second
- when .target is set. If not, this is hoe mutch to slow down/accelerate
- someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
+strength : This is how much force to add in the direction of .target each second
+ when .target is set. If not, this is how much to slow down/accelerate
+ something cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble)
-radius : If set, act as a spherical device rather then a liniar one.
+radius : If set, act as a spherical device rather then a linear one.
falloff : 0 = none, 1 = liniar, 2 = inverted liniar
{
WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE);
- WriteInt24_t(MSG_ENTITY, this.spawnflags);
+ WriteByte(MSG_ENTITY, this.spawnflags);
WriteCoord(MSG_ENTITY, this.radius);
WriteCoord(MSG_ENTITY, this.strength);
WriteByte(MSG_ENTITY, this.falloff);
if(this.radius)
{
- if(!this.strength) this.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier;
+ if(!this.strength)
+ {
+ this.strength = IMPULSE_DEFAULT_RADIAL_STRENGTH * autocvar_g_triggerimpulse_radial_multiplier;
+ }
setorigin(this, this.origin);
setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius);
- settouch(this, trigger_impulse_touch3);
+ settouch(this, trigger_impulse_touch_radial);
}
else
{
if(this.target)
{
- if(!this.strength) this.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier;
- settouch(this, trigger_impulse_touch1);
+ if(!this.strength)
+ {
+ this.strength = IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH * autocvar_g_triggerimpulse_directional_multiplier;
+ }
+ settouch(this, trigger_impulse_touch_directional);
}
else
{
- if(!this.strength) this.strength = 0.9;
+ if(!this.strength)
+ {
+ this.strength = IMPULSE_DEFAULT_ACCEL_STRENGTH;
+ }
this.strength = (this.strength ** autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier;
- settouch(this, trigger_impulse_touch2);
+ settouch(this, trigger_impulse_touch_accel);
}
}
#elif defined(CSQC)
NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew)
{
- this.spawnflags = ReadInt24_t();
+ this.spawnflags = ReadByte();
this.radius = ReadCoord();
this.strength = ReadCoord();
this.falloff = ReadByte();
this.entremove = trigger_remove_generic;
this.move_time = time;
- if (this.radius) { settouch(this, trigger_impulse_touch3); }
- else if (this.target) { settouch(this, trigger_impulse_touch1); }
- else { settouch(this, trigger_impulse_touch2); }
+ if (this.radius)
+ {
+ settouch(this, trigger_impulse_touch_radial);
+ }
+ else if (this.target)
+ {
+ settouch(this, trigger_impulse_touch_directional);
+ }
+ else
+ {
+ settouch(this, trigger_impulse_touch_accel);
+ }
}
#endif
// tZorks trigger impulse / gravity
.float radius;
-.float falloff;
+.int falloff;
.float strength;
.float lastpushtime;
+
+const int FALLOFF_NO = 0;
+const int FALLOFF_LINEAR = 1;
+const int FALLOFF_LINEAR_INV = 2;
+
+const int IMPULSE_DIRECTIONAL_SPEEDTARGET = BIT(6);
+
+const float IMPULSE_DEFAULT_RADIAL_STRENGTH = 2000;
+const float IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH = 950;
+const float IMPULSE_DEFAULT_ACCEL_STRENGTH = 0.9;
+
+const float IMPULSE_MAX_PUSHDELTATIME = 0.15;
+
+const float IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR = 8;
#include "jumppads.qh"
// TODO: split target_push and put it in the target folder
#ifdef SVQC
-#include "jumppads.qh"
#include <common/physics/movetypes/movetypes.qh>
void trigger_push_use(entity this, entity actor, entity trigger)
if(teamplay)
{
this.team = actor.team;
- this.SendFlags |= 2;
+ this.SendFlags |= SF_TRIGGER_UPDATE;
}
}
#endif
return;
if(this.team)
- if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, toucher)))
return;
EXACTTRIGGER_TOUCH(this, toucher);
{
// first calculate a typical start point for the jump
vector org = (this.absmin + this.absmax) * 0.5;
- org.z = this.absmax.z - PL_MIN_CONST.z - 10;
+ org.z = this.absmax.z - PL_MIN_CONST.z - 7;
if (this.target)
{
void trigger_push_updatelink(entity this)
{
- this.SendFlags |= 1;
+ this.SendFlags |= SF_TRIGGER_INIT;
}
void trigger_push_link(entity this)
target_push_init(this); // normal push target behaviour can be combined with a legacy pusher?
}
-spawnfunc(target_push) { target_push_init2(this); }
-spawnfunc(info_notnull) { target_push_init(this); }
-spawnfunc(target_position) { target_push_init(this); }
+spawnfunc(target_push)
+{
+ target_push_init2(this);
+}
+
+spawnfunc(info_notnull)
+{
+ target_push_init(this);
+}
+spawnfunc(target_position)
+{
+ target_push_init(this);
+}
#elif defined(CSQC)
NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew)
{
this.classname = "jumppad";
- int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
+ int mytm = ReadByte();
+ if(mytm)
+ {
+ this.team = mytm - 1;
+ }
this.spawnflags = ReadInt24_t();
this.active = ReadByte();
this.height = ReadCoord();
#pragma once
+
+const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead
+const int PUSH_SILENT = BIT(1); // not used?
+
IntrusiveList g_jumppads;
STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); }
-const float PUSH_ONCE = 1;
-const float PUSH_SILENT = 2;
-
.float pushltime;
.float istypefrag;
.float height;
magicear_matched = false;
dotrigger = ((IS_PLAYER(source)) && (!IS_DEAD(source)) && ((ear.radius == 0) || (vdist(source.origin - ear.origin, <=, ear.radius))));
- domatch = ((ear.spawnflags & 32) || dotrigger);
+ domatch = ((ear.spawnflags & MAGICEAR_REPLACE_OUTSIDE) || dotrigger);
if (!domatch)
return msgin;
if (!msgin)
{
// we are in TUBA mode!
- if (!(ear.spawnflags & 256))
+ if (!(ear.spawnflags & MAGICEAR_TUBA))
return msgin;
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
.entity weaponentity = weaponentities[slot];
- if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & 512), ear.movedir_y, ear.movedir_z))
+ if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & MAGICEAR_TUBA_EXACTPITCH), ear.movedir_y, ear.movedir_z))
return msgin;
}
return msgin;
}
- if(ear.spawnflags & 256) // ENOTUBA
+ if(ear.spawnflags & MAGICEAR_TUBA) // ENOTUBA
return msgin;
if(privatesay)
{
- if(ear.spawnflags & 4)
+ if(ear.spawnflags & MAGICEAR_IGNORE_TELL)
return msgin;
}
else
{
if(!teamsay)
- if(ear.spawnflags & 1)
+ if(ear.spawnflags & MAGICEAR_IGNORE_SAY)
return msgin;
if(teamsay > 0)
- if(ear.spawnflags & 2)
+ if(ear.spawnflags & MAGICEAR_IGNORE_TEAMSAY)
return msgin;
if(teamsay < 0)
- if(ear.spawnflags & 8)
+ if(ear.spawnflags & MAGICEAR_IGNORE_INVALIDTELL)
return msgin;
}
matchstart = -1;
l = strlen(ear.message);
- if(ear.spawnflags & 128)
+ if(ear.spawnflags & MAGICEAR_NODECOLORIZE)
msg = msgin;
else
msg = strdecolorize(msgin);
ear.message = savemessage;
}
- if(ear.spawnflags & 16)
+ if(ear.spawnflags & MAGICEAR_REPLACE_WHOLE_MESSAGE)
{
return ear.netname;
}
for(ear = magicears; ear; ear = ear.enemy)
{
msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin);
- if(!(ear.spawnflags & 64))
+ if(!(ear.spawnflags & MAGICEAR_CONTINUE))
if(magicear_matched)
return msgout;
msgin = msgout;
#pragma once
+
+
+const int MAGICEAR_IGNORE_SAY = BIT(0);
+const int MAGICEAR_IGNORE_TEAMSAY = BIT(1);
+const int MAGICEAR_IGNORE_TELL = BIT(2);
+const int MAGICEAR_IGNORE_INVALIDTELL = BIT(3);
+const int MAGICEAR_REPLACE_WHOLE_MESSAGE = BIT(4);
+const int MAGICEAR_REPLACE_OUTSIDE = BIT(5);
+const int MAGICEAR_CONTINUE = BIT(6);
+const int MAGICEAR_NODECOLORIZE = BIT(7);
+const int MAGICEAR_TUBA = BIT(8);
+const int MAGICEAR_TUBA_EXACTPITCH = BIT(9);
{
if(!this.wait)
this.wait = 1;
- if(this.spawnflags & 1)
+ if(this.spawnflags & MONOFLOP_FIXED)
this.use = monoflop_fixed_use;
else
this.use = monoflop_use;
#pragma once
+
+
+const int MONOFLOP_FIXED = BIT(0);
return; // allready been triggered
}
- if(this.spawnflags & 16384)
- if(!IS_PLAYER(this.enemy))
+ if(this.spawnflags & ONLY_PLAYERS && !IS_PLAYER(this.enemy))
+ {
return; // only players
+ }
+ // TODO: restructure this so that trigger_secret is more independent
if (this.classname == "trigger_secret")
{
if (!IS_PLAYER(this.enemy))
}
if (this.noise)
+ {
_sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+ }
-// don't trigger again until reset
+ // don't trigger again until reset
this.takedamage = DAMAGE_NO;
SUB_UseTargets(this, this.enemy, this.goalentity);
void multi_touch(entity this, entity toucher)
{
- if(!(this.spawnflags & 2))
- if(!toucher.iscreature)
- return;
+ if(!(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature)
+ {
+ return;
+ }
if(this.team)
- if(((this.spawnflags & 4) == 0) == (this.team != toucher.team))
+ {
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team))
+ {
return;
+ }
+ }
-// if the trigger has an angles field, check player's facing direction
+ // if the trigger has an angles field, check player's facing direction
if (this.movedir != '0 0 0')
{
makevectors (toucher.angles);
// if the trigger has pressed keys, check that the player is pressing those keys
if(this.pressedkeys && IS_PLAYER(toucher)) // only for players
- if(!(CS(toucher).pressedkeys & this.pressedkeys))
- return;
+ {
+ if(!(CS(toucher).pressedkeys & this.pressedkeys))
+ {
+ return;
+ }
+ }
EXACTTRIGGER_TOUCH(this, toucher);
{
if(!this.takedamage)
return;
- if(this.spawnflags & DOOR_NOSPLASH)
+ if(this.spawnflags & NOSPLASH)
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
if(this.team)
- if(((this.spawnflags & 4) == 0) == (this.team != attacker.team))
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team))
return;
this.health = this.health - damage;
if (this.health <= 0)
void multivibrator_reset(entity this)
{
- if(!(this.spawnflags & 1))
+ if(!(this.spawnflags & START_ENABLED))
this.nextthink = 0; // wait for a trigger event
else
this.nextthink = max(1, time);
}
-/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON
+/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED
"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off.
-------- KEYS --------
target: trigger all entities with this targetname when it goes off
wait: "on" cycle time (default: 1)
respawntime: "off" cycle time (default: same as wait)
-------- SPAWNFLAGS --------
-START_ON: assume it is already turned on (when targeted)
+START_ENABLED: assume it is already turned on (when targeted)
*/
spawnfunc(trigger_multivibrator)
{
this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
}
-spawnfunc(target_relay) { spawnfunc_trigger_relay(this); }
+spawnfunc(target_relay)
+{
+ spawnfunc_trigger_relay(this);
+}
#endif
else
{
//bprint("Not using setactive\n");
- if(this.cnt == ACTIVE_TOGGLE)
- if(trg.active == ACTIVE_ACTIVE)
- trg.active = ACTIVE_NOT;
- else
- trg.active = ACTIVE_ACTIVE;
- else
- trg.active = this.cnt;
+ generic_setactive(trg, this.cnt);
}
}
}
// TODO make this generic AND faster than nextent()ing through all, if somehow possible
n = (cvar_string(this.netname) == cvar_string(this.message));
- if(this.spawnflags & 1)
+ if(this.spawnflags & RELAYIF_NEGATE)
n = !n;
if(n)
#pragma once
+
+
+const int RELAYIF_NEGATE = BIT(0);
{
if(actor.team)
{
- if(this.spawnflags & 2)
+ if(this.spawnflags & RELAYTEAMCHECK_INVERT)
{
if(DIFF_TEAM(actor, this))
SUB_UseTargets(this, actor, trigger);
}
else
{
- if(this.spawnflags & 1)
+ if(this.spawnflags & RELAYTEAMCHECK_NOTEAM)
SUB_UseTargets(this, actor, trigger);
}
}
#pragma once
+
+
+const int RELAYTEAMCHECK_NOTEAM = BIT(0);
+const int RELAYTEAMCHECK_INVERT = BIT(1);
return false;
if(this.team)
- if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, player)))
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, player)))
return false;
return true;
string s = this.target; this.target = string_null;
SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
- if (!this.target) this.target = s;
+ if (!this.target)
+ {
+ this.target = s;
+ }
SUB_UseTargets(e, player, player);
}
this.classname = "trigger_teleport";
if(isnew)
IL_PUSH(g_teleporters, this);
- int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
+ int mytm = ReadByte();
+ if(mytm)
+ {
+ this.team = mytm - 1;
+ }
this.spawnflags = ReadInt24_t();
this.active = ReadByte();
this.speed = ReadCoord();
}
// compatibility
-spawnfunc(target_viewlocation) { spawnfunc_target_viewlocation_start(this); }
+spawnfunc(target_viewlocation)
+{
+ spawnfunc_target_viewlocation_start(this);
+}
#elif defined(CSQC)
#pragma once
-.entity viewloc;
const int VIEWLOC_NOSIDESCROLL = BIT(0); // NOTE: currently unimplemented
const int VIEWLOC_FREEAIM = BIT(1);
const int VIEWLOC_FREEMOVE = BIT(2);
+.entity viewloc;
+
#ifdef CSQC
.entity goalentity;
.entity enemy;
}
#ifdef SVQC
+void generic_setactive(entity this, int act)
+{
+ if(act == ACTIVE_TOGGLE)
+ {
+ if(this.active == ACTIVE_ACTIVE)
+ {
+ this.active = ACTIVE_NOT;
+ }
+ else
+ {
+ this.active = ACTIVE_ACTIVE;
+ }
+ }
+ else
+ {
+ this.active = act;
+ }
+}
+
+void generic_netlinked_setactive(entity this, int act)
+{
+ int old_status = this.active;
+ generic_setactive(this, act);
+
+ if (this.active != old_status)
+ {
+ this.SendFlags |= SF_TRIGGER_UPDATE;
+ }
+}
+
+void generic_netlinked_reset(entity this)
+{
+ IFTARGETED
+ {
+ if(this.spawnflags & START_ENABLED)
+ {
+ this.active = ACTIVE_ACTIVE;
+ }
+ else
+ {
+ this.active = ACTIVE_NOT;
+ }
+ }
+ else
+ {
+ this.active = ACTIVE_ACTIVE;
+ }
+
+ this.SendFlags |= SF_TRIGGER_UPDATE;
+}
+
+// Compatibility with old maps
+void generic_netlinked_legacy_use(entity this, entity actor, entity trigger)
+{
+ LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname);
+ this.setactive(this, ACTIVE_TOGGLE);
+}
bool autocvar_g_triggers_debug = true;
#pragma once
-
-const float SF_TRIGGER_INIT = 1;
-const float SF_TRIGGER_UPDATE = 2;
-const float SF_TRIGGER_RESET = 4;
-
-const float SPAWNFLAG_NOMESSAGE = 1;
-const float SPAWNFLAG_NOTOUCH = 1;
+#include "defs.qh"
.bool pushable;
void target_voicescript_clear(entity pl);
void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger);
+
+void generic_setactive(entity this, int act);
+// generic methods for netlinked entities
+void generic_netlinked_reset(entity this);
+void generic_netlinked_setactive(entity this, int act);
+// WARNING: DON'T USE, ONLY TO KEEP COMPATIBILITY BECAUSE OF SWITCH FROM .state TO .alive!!!!
+void generic_netlinked_legacy_use(entity this, entity actor, entity trigger);
#endif
.float sub_target_used;
.float active;
.string target;
.string targetname;
-
-const int ACTIVE_NOT = 0;
-const int ACTIVE_ACTIVE = 1;
-const int ACTIVE_IDLE = 2;
-const int ACTIVE_BUSY = 2;
-const int ACTIVE_TOGGLE = 3;
#endif
METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this, entity it))
{
- if(g_instagib)
+ if(autocvar_g_instagib)
{
.entity weaponentity = weaponentities[0]; // TODO: unhardcode
FireRailgunBullet (it, weaponentity, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000,
METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret thistur, entity it))
{
- if (g_instagib) {
+ if (autocvar_g_instagib) {
.entity weaponentity = weaponentities[0]; // TODO: unhardcode
FireRailgunBullet (it, weaponentity, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000,
800, 0, 0, 0, 0, DEATH_TURRET_PLASMA.m_id);
if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT)))
W_RocketMinsta_Explosion(actor, weaponentity, trace_endpos);
- W_DecreaseAmmo(thiswep, actor, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)), weaponentity);
+ W_DecreaseAmmo(thiswep, actor, ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)), weaponentity);
}
void W_RocketMinsta_Laser_Explode (entity this, entity directhitentity)
}
METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
+ float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
// if the laser uses load, we also consider its ammo for reloading
if(WEP_CVAR(vaporizer, reload_ammo) && WEP_CVAR_SEC(vaporizer, ammo) && actor.(weaponentity).clip_load < min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo))) { // forced reload
thiswep.wr_reload(thiswep, actor, weaponentity);
}
METHOD(Vaporizer, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
+ float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= vaporizer_ammo;
ammo_amount += actor.(weaponentity).(weapon_load[WEP_VAPORIZER.m_id]) >= vaporizer_ammo;
return ammo_amount;
}
METHOD(Vaporizer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
{
- float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
+ float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
float used_ammo;
if(WEP_CVAR_SEC(vaporizer, ammo))
used_ammo = min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo));
float maxspeed_mod = (!this.in_swamp) ? 1 : this.swamp_slowdown; // cvar("g_balance_swamp_moverate");
// conveyors: first fix velocity
- if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; }
+ if (this.conveyor.active) { this.velocity -= this.conveyor.movedir; }
MUTATOR_CALLHOOK(PlayerPhysics, this, dt);
if (!IS_PLAYER(this)) {
LABEL(end)
if (IS_ONGROUND(this)) { this.lastground = time; }
// conveyors: then break velocity again
- if (this.conveyor.state) { this.velocity += this.conveyor.movedir; }
+ if (this.conveyor.active) { this.velocity += this.conveyor.movedir; }
this.lastflags = this.flags;
this.lastclassname = this.classname;
#ifndef QCC_SUPPORT_POW
#define pow(a, b) pow(a, b)
#else
- #define pow(a, b) (a ** b)
+ #define pow(a, b) ((a) ** (b))
#endif
#include "self.qh"
#include "oo.qh"
#include "p2mathlib.qc"
#include "progname.qh"
+#include "promise.qc"
#include "random.qc"
#include "registry.qh"
#include "registry_net.qh"
#include <lib/angle.qc>
#include <lib/json.qc>
#include <lib/p2mathlib.qc>
+#include <lib/promise.qc>
#include <lib/random.qc>
#include <lib/sortlist.qc>
#include <lib/test.qc>
#include <lib/angle.qh>
#include <lib/json.qh>
#include <lib/p2mathlib.qh>
+#include <lib/promise.qh>
#include <lib/random.qh>
#include <lib/sortlist.qh>
#include <lib/test.qh>
#ifdef SVQC
const int MSG_ENTITY = 5;
- .int Version; // deprecated, use SendFlags
.int SendFlags;
IntrusiveList g_uncustomizables;
--- /dev/null
+#include "promise.qh"
+
+.int _ref_count;
+.void(entity this) _ref_finalize;
+
+void ref_init(entity this, int init, void(entity this) finalize)
+{
+ this._ref_count = init;
+ this._ref_finalize = finalize;
+}
+
+// todo: rename to `ref`
+entity REF(entity this)
+{
+ this._ref_count += 1;
+ return this;
+}
+
+entity unref(Promise this)
+{
+ this._ref_count -= 1;
+ if (!this._ref_count) {
+ LOG_DEBUGF("Finalize entity %i (from %s)", this, this.sourceLoc);
+ this._ref_finalize(this);
+ return NULL;
+ }
+ return this;
+}
+
+enum {
+ PROMISE_PENDING,
+ PROMISE_RESOLVED,
+ PROMISE_REJECTED,
+};
+
+classfield(Promise) .int _promise_state;
+classfield(Promise) .entity _promise_result;
+classfield(Promise) .IntrusiveList _promise_handlers;
+
+entityclass(PromiseHandler);
+classfield(PromiseHandler) .Promise _promise_handler_ret;
+classfield(PromiseHandler) .entity _promise_handler_data;
+classfield(PromiseHandler) .Promise(Promise ret, entity result, entity userdata) _promise_handler_resolve;
+classfield(PromiseHandler) .Promise(Promise ret, entity err, entity userdata) _promise_handler_reject;
+
+void _Promise_finalize(Promise this)
+{
+ delete(this);
+}
+
+Promise Promise_new_(Promise this)
+{
+ ref_init(this, 2, _Promise_finalize);
+ this._promise_result = this; // promises default to being their own result to save on entities
+ return this;
+}
+
+void _Promise_handle(Promise this, PromiseHandler h);
+
+void Promise_resolve(Promise this)
+{
+ if (!this) {
+ LOG_SEVERE("Attempted to resolve a null promise");
+ return;
+ }
+ if (this._promise_state != PROMISE_PENDING) {
+ LOG_SEVEREF("Resolved non-pending promise %i", this);
+ return;
+ }
+ this._promise_state = PROMISE_RESOLVED;
+ if (this._promise_handlers) {
+ IL_EACH(this._promise_handlers, true, _Promise_handle(this, it));
+ IL_DELETE(this._promise_handlers);
+ }
+ unref(this);
+ return;
+}
+
+void Promise_reject(Promise this)
+{
+ if (!this) {
+ LOG_SEVERE("Attempted to reject a null promise");
+ return;
+ }
+ if (this._promise_state != PROMISE_PENDING) {
+ LOG_SEVEREF("Rejected non-pending promise %i", this);
+ return;
+ }
+ this._promise_state = PROMISE_REJECTED;
+ if (this._promise_handlers) {
+ IL_EACH(this._promise_handlers, true, _Promise_handle(this, it));
+ IL_DELETE(this._promise_handlers);
+ }
+ unref(this);
+ return;
+}
+
+Promise _Promise_then(
+ Promise this,
+ Promise ret,
+ Promise(Promise ret, entity result, entity userdata) onResolve,
+ Promise(Promise ret, entity result, entity userdata) onReject,
+ entity userdata
+);
+
+void _Promise_handle(Promise this, PromiseHandler h)
+{
+ switch (this._promise_state) {
+ case PROMISE_PENDING:
+ if (!this._promise_handlers) {
+ this._promise_handlers = IL_NEW();
+ }
+ IL_PUSH(this._promise_handlers, h);
+ break;
+ case PROMISE_RESOLVED: {
+ Promise ret = h._promise_handler_ret;
+ Promise p = h._promise_handler_resolve(ret, this._promise_result, h._promise_handler_data);
+ if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret
+ delete(h);
+ break;
+ }
+ case PROMISE_REJECTED: {
+ Promise ret = h._promise_handler_ret;
+ Promise p = h._promise_handler_reject(ret, this._promise_result, h._promise_handler_data);
+ if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret
+ delete(h);
+ break;
+ }
+ }
+}
+
+void _Promise_done(
+ Promise this,
+ Promise(Promise ret, entity result, entity userdata) onResolve,
+ Promise(Promise ret, entity err, entity userdata) onReject,
+ Promise ret,
+ entity userdata
+)
+{
+ PromiseHandler h = new_pure(PromiseHandler);
+ h._promise_handler_ret = ret;
+ h._promise_handler_data = userdata;
+ h._promise_handler_resolve = onResolve;
+ h._promise_handler_reject = onReject;
+ _Promise_handle(this, h);
+}
+
+Promise _Promise_onResolve_default(Promise ret, entity result, entity userdata)
+{
+ ret._promise_result = result;
+ Promise_resolve(ret);
+ return ret;
+}
+
+Promise _Promise_onReject_default(Promise ret, entity err, entity userdata)
+{
+ ret._promise_result = err;
+ Promise_reject(ret);
+ return ret;
+}
+
+Promise _Promise_then(
+ Promise this,
+ Promise ret,
+ Promise(Promise ret, entity result, entity userdata) onResolve,
+ Promise(Promise ret, entity result, entity userdata) onReject,
+ entity userdata
+)
+{
+ _Promise_done(
+ this,
+ (onResolve ? onResolve : _Promise_onResolve_default),
+ (onReject ? onReject : _Promise_onReject_default),
+ ret,
+ userdata
+ );
+ return ret;
+}
+
+Promise Promise_then_(
+ Promise this,
+ Promise ret,
+ Promise(Promise ret, entity result, entity userdata) onResolve,
+ entity userdata
+)
+{
+ unref(ret); // ret is a temporary
+ return _Promise_then(this, ret, onResolve, func_null, userdata);
+}
+
+Promise Promise_catch_(
+ Promise this,
+ Promise ret,
+ Promise(Promise ret, entity result, entity userdata) onReject,
+ entity userdata
+)
+{
+ unref(ret); // ret is a temporary
+ return _Promise_then(this, ret, func_null, onReject, userdata);
+}
+
+// utils
+
+#ifndef MENUQC
+
+Promise Promise_sleep(float n)
+{
+ Promise p = unref(Promise_new());
+ setthink(p, Promise_resolve);
+ p.nextthink = time + n;
+ return p;
+}
+
+#endif
--- /dev/null
+#pragma once
+
+entityclass(Promise);
+
+#define Promise_new() Promise_new_(new_pure(Promise))
+Promise Promise_new_(Promise this);
+
+/**
+ * notify all Promise_then subscribers that this promise has resolved
+ */
+void Promise_resolve(Promise this);
+
+#define Promise_then(this, handler, userdata) Promise_then_(this, Promise_new(), handler, userdata)
+Promise Promise_then_(Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) handler, entity userdata);
+
+/**
+ * notify all Promise_catch subscribers that this promise has rejected
+ */
+void Promise_reject(Promise this);
+
+#define Promise_catch(this, handler, userdata) Promise_catch_(this, Promise_new(), handler, userdata)
+Promise Promise_catch_(Promise this, Promise ret, Promise(Promise ret, entity err, entity userdata) handler, entity userdata);
+
+// utils
+
+#ifndef MENUQC
+
+// TODO: support menu
+Promise Promise_sleep(float n);
+
+#endif
FIELD_SCALAR(fld, bgmscriptsustain) \
FIELD_SCALAR(fld, bgmscript) \
FIELD_SCALAR(fld, button0) \
+ FIELD_SCALAR(fld, chmap) \
FIELD_SCALAR(fld, cnt) \
FIELD_SCALAR(fld, colormap) \
FIELD_SCALAR(fld, count) \
FIELD_SCALAR(fld, dmg_force) \
FIELD_SCALAR(fld, dmg_radius) \
FIELD_SCALAR(fld, effects) \
+ FIELD_SCALAR(fld, falloff) \
FIELD_SCALAR(fld, flags) \
FIELD_SCALAR(fld, fog) \
FIELD_SCALAR(fld, frags) \
FIELD_SCALAR(fld, frame) \
+ FIELD_SCALAR(fld, gametype) \
FIELD_SCALAR(fld, gametypefilter) \
FIELD_SCALAR(fld, geomtype) \
FIELD_SCALAR(fld, gravity) \
FIELD_SCALAR(fld, noalign) \
FIELD_SCALAR(fld, noise1) \
FIELD_SCALAR(fld, noise2) \
+ FIELD_SCALAR(fld, noise3) \
FIELD_SCALAR(fld, noise) \
FIELD_SCALAR(fld, phase) \
FIELD_SCALAR(fld, platmovetype) \
FIELD_SCALAR(fld, target_random) \
FIELD_SCALAR(fld, target_range) \
FIELD_SCALAR(fld, team) \
+ FIELD_SCALAR(fld, trigger_reverse) \
FIELD_SCALAR(fld, turret_scale_health) \
FIELD_SCALAR(fld, turret_scale_range) \
FIELD_SCALAR(fld, turret_scale_respawn) \
FIELD_VEC(fld, absmin) \
FIELD_VEC(fld, angles) \
FIELD_VEC(fld, avelocity) \
+ FIELD_VEC(fld, beam_color)\
FIELD_VEC(fld, debrisavelocityjitter) \
FIELD_VEC(fld, debrisvelocity) \
FIELD_VEC(fld, debrisvelocityjitter) \
#include "textlabel.qh"
#include "inputbox.qh"
#include "checkbox.qh"
+#include "commandbutton.qh"
#include "button.qh"
entity makeXonoticServerListTab()
e.onClickEntity = slist;
slist.infoButton = e;
me.TR(me);
- me.TD(me, 1, me.columns, e = makeXonoticButton(_("Join!"), '0 0 0'));
+ me.TD(me, 1, 1, e = makeXonoticCommandButton_T(_("Disconnect"), '0 0 0', "disconnect", 0,
+ _("Disconnect from the server")));
+ slist.disconnectButton = e;
+ me.TD(me, 1, me.columns-1, e = makeXonoticButton(_("Join!"), '0 0 0'));
e.onClick = ServerList_Connect_Click;
e.onClickEntity = slist;
slist.connectButton = e;
else { me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); }
me.connectButton.disabled = (me.lockedSelectedItem || (me.nItems == 0 && me.ipAddressBox.text == ""));
+ me.disconnectButton.disabled = (!(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)));
me.infoButton.disabled = (me.lockedSelectedItem || me.nItems == 0 || !owned);
me.favoriteButton.disabled = (me.lockedSelectedItem || (me.nItems == 0 && me.ipAddressBox.text == ""));
ATTRIB(XonoticServerList, sortButton4, entity);
ATTRIB(XonoticServerList, sortButton5, entity);
ATTRIB(XonoticServerList, connectButton, entity);
+ ATTRIB(XonoticServerList, disconnectButton, entity);
ATTRIB(XonoticServerList, infoButton, entity);
ATTRIB(XonoticServerList, currentSortOrder, float, 0);
ATTRIB(XonoticServerList, currentSortField, float, -1);
float autocvar_g_maxplayers_spectator_blocktime;
float autocvar_g_maxpushtime;
float autocvar_g_maxspeed;
-bool autocvar_g_instagib;
+#define autocvar_g_instagib cvar("g_instagib")
#define autocvar_g_mirrordamage cvar("g_mirrordamage")
#define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual")
bool autocvar_g_mirrordamage_onlyweapons;
bool autocvar_g_use_ammunition;
bool autocvar_g_waypointeditor;
bool autocvar_g_waypointeditor_symmetrical;
+bool autocvar_g_waypointeditor_symmetrical_allowload = true;
vector autocvar_g_waypointeditor_symmetrical_origin;
int autocvar_g_waypointeditor_symmetrical_order;
vector autocvar_g_waypointeditor_symmetrical_axis;
return;
}
- // add 3 comments to not break compatibility with older Xonotic versions
+ float sym = autocvar_g_waypointeditor_symmetrical;
+ string sym_str = ftos(sym);
+ if (sym == -1 || (sym == 1 && autocvar_g_waypointeditor_symmetrical_order >= 2))
+ {
+ if (sym == 1)
+ {
+ sym_str = cons(sym_str, "-");
+ sym_str = cons(sym_str, "-");
+ }
+ else
+ {
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.x));
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.y));
+ }
+ if (autocvar_g_waypointeditor_symmetrical_order >= 2)
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_order));
+ }
+ else if (autocvar_g_waypointeditor_symmetrical == -2)
+ {
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.x));
+ sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.y));
+ }
+
+ // a group of 3 comments doesn't break compatibility with older Xonotic versions
// (they are read as a waypoint with origin '0 0 0' and flag 0 though)
fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n"));
- fputs(file, strcat("//", "\n"));
+ fputs(file, strcat("//", "WAYPOINT_SYMMETRY ", sym_str, "\n"));
fputs(file, strcat("//", "\n"));
int c = 0;
bool parse_comments = true;
float ver = 0;
+ float sym = 0;
+ float sym_param1 = 0, sym_param2 = 0, sym_param3 = 0;
while ((s = fgets(file)))
{
{
if(substring(s, 2, 17) == "WAYPOINT_VERSION ")
ver = stof(substring(s, 19, -1));
+ else if(substring(s, 2, 18) == "WAYPOINT_SYMMETRY ")
+ {
+ int tokens = tokenizebyseparator(substring(s, 20, -1), " ");
+ if (tokens) { sym = stof(argv(0)); }
+ if (tokens > 1) { sym_param1 = stof(argv(1)); }
+ if (tokens > 2) { sym_param2 = stof(argv(2)); }
+ if (tokens > 3) { sym_param3 = stof(argv(3)); }
+ }
continue;
}
else
fclose(file);
LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints");
+ if (autocvar_g_waypointeditor && autocvar_g_waypointeditor_symmetrical_allowload)
+ {
+ cvar_set("g_waypointeditor_symmetrical", ftos(sym));
+ if (sym == 1 && sym_param3 < 2)
+ cvar_set("g_waypointeditor_symmetrical_order", "0"); // make sure this is reset if not loaded
+ if (sym == -1 || (sym == 1 && sym_param3 >= 2))
+ {
+ string params;
+ if (sym == 1)
+ params = cons("-", "-");
+ else
+ {
+ params = cons(ftos(sym_param1), ftos(sym_param2));
+ cvar_set("g_waypointeditor_symmetrical_origin", params);
+ }
+ cvar_set("g_waypointeditor_symmetrical_order", ftos(sym_param3));
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with origin ", params, " and order ", ftos(sym_param3));
+ }
+ else if (sym == -2)
+ {
+ string params = strcat(ftos(sym_param1), " ", ftos(sym_param2));
+ cvar_set("g_waypointeditor_symmetrical_axis", params);
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with axis ", params);
+ }
+ else
+ LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym));
+ LOG_INFO(strcat("g_waypointeditor_symmetrical", " has been set to ", cvar_string("g_waypointeditor_symmetrical")));
+ }
+
return cwp + cwb;
}
// increase by 0.01 when changes require only waypoint relinking
// increase by 1 when changes require to manually edit waypoints
// max 2 decimal places, always specified
-#define WAYPOINT_VERSION 1.00
+#define WAYPOINT_VERSION 1.01
// fields you can query using prvm_global server to get some statistics about waypoint linking culling
float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled;
Fire_ApplyDamage(this);
Fire_ApplyEffect(this);
- if (!g_instagib)
+ if (!autocvar_g_instagib)
{
if (this.items & ITEM_Strength.m_itemid)
{
break;
});
- if (!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; }
+ if (!found && arg_lower != "random" && arg_lower != "anyrandom") { print_to(caller, "Invalid monster"); return; }
totalspawned += 1;
WarpZone_TraceBox(CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller);
// Globals
-float g_footsteps, g_grappling_hook, g_instagib;
+float g_footsteps, g_grappling_hook;
float g_warmup_allguns;
float g_warmup_allow_timeout;
float warmup_stage;
}
}
- if(!g_instagib)
+ if(!autocvar_g_instagib)
{
// apply strength multiplier
if (attacker.items & ITEM_Strength.m_itemid)
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
- g_instagib = cvar("g_instagib");
-
sv_clones = cvar("sv_clones");
sv_foginterval = cvar("sv_foginterval");
g_footsteps = cvar("g_footsteps");
/**/
MUTATOR_HOOKABLE(PlayerPhysics_UpdateStats, EV_PlayerPhysics_UpdateStats);
+/** called after physics stats are set on a player, allows post-initialization modifications */
+#define EV_PlayerPhysics_PostUpdateStats(i, o) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** maxspeed_mod */ i(float, MUTATOR_ARGV_1_float) \
+ /**/
+MUTATOR_HOOKABLE(PlayerPhysics_PostUpdateStats, EV_PlayerPhysics_PostUpdateStats);
+
/** return true to use your own aim target (or none at all) */
#define EV_HavocBot_Aim(i, o) \
/** bot */ i(entity, MUTATOR_ARGV_0_entity) \
}
return;
LABEL(cleanup)
- builtin_remove(this);
+ delete(this);
}
void WarpZone_PostInitialize_Callback()
set g_waypointeditor 0
set g_waypointeditor_auto 0 "Automatically create waypoints for bots while playing; BEWARE, this currently creates too many of them"
set g_waypointeditor_symmetrical 0 "Enable symmetrical editing of waypoints on symmetrical CTF maps (NOTE: it assumes that the map is perfectly symmetrical). 1: automatically determine origin of symmetry; -1: use custom origin (g_waypointeditor_symmetrical_origin); 2: automatically determine axis of symmetry; -2: use custom axis (g_waypointeditor_symmetrical_axis)"
+set g_waypointeditor_symmetrical_allowload 1 "Allow loading symmetry settings from waypoint files into g_waypointeditor_symmetrical* cvars on map start"
set g_waypointeditor_symmetrical_origin "0 0" "Custom origin of symmetry (x y)"
set g_waypointeditor_symmetrical_order 0 "if >= 2 apply rotational symmetry (around origin of symmetry) of this order, otherwise apply autodetected order of symmetry"
set g_waypointeditor_symmetrical_axis "0 0" "Custom axis of symmetry (m q parameters of y = mx + q)"
set g_spawnshieldtime 1 "number of seconds you are invincible after you spawned, this shield is lost after you fire"
set g_spawnshield_blockdamage 1 "how much spawn shield protects you from damage (1 = full protection)"
-set g_antilag 2 "AntiLag (0 = no AntiLag, 1 = verified client side hit scan, 2 = server side hit scan in the past, 3 = unverified client side hit scan)"
+set g_antilag 2 "AntiLag (0 = no AntiLag, 1 = verified client side hit scan, 2 = server side hit scan in the past)"
set g_antilag_nudge 0 "don't touch"
set g_shootfromeye 0 "shots are fired from your eye/crosshair; visual gun position can still be influenced by cl_gunalign 1 and 2"
set g_shootfromcenter 0 "weapon gets moved to the center, shots still come from the barrel of your weapon; visual gun position can still be influenced by cl_gunalign 1 and 2"