- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
\r
- - EXPECT=458e9e611a757c745da05c85a37e576d\r
+ - EXPECT=8e36763a3b4590356bcc449b2452d0ea\r
- HASH=$(${ENGINE} +timestamps 1 +exec serverbench.cfg\r
| tee /dev/stderr\r
| sed -e 's,^\[[^]]*\] ,,'\r
-Wed Jun 8 07:23:14 CEST 2022
+Mon Jun 20 07:23:13 CEST 2022
#: qcsrc/common/monsters/monster/shambler.qh:17
#: qcsrc/menu/xonotic/dialog_monstertools.qc:17
msgid "Golem"
-msgstr ""
+msgstr "Golem"
#: qcsrc/common/monsters/monster/spider.qh:17
#: qcsrc/menu/xonotic/dialog_monstertools.qc:16
#: qcsrc/common/notifications/all.inc:303
#, c-format
msgid "^BG%s^K1's innards became outwards by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1s Eingeweide wurden von einem Golem%s%s nach außen gekehrt"
#: qcsrc/common/notifications/all.inc:304
#, c-format
msgid "^BG%s^K1 was smashed by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1 wurde von einem Golem zerquetscht%s%s"
#: qcsrc/common/notifications/all.inc:305
#, c-format
msgid "^BG%s^K1 was zapped to death by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1 wurde von einem Golem%s%s zu Tode geschockt"
#: qcsrc/common/notifications/all.inc:306
#, c-format
#: qcsrc/menu/xonotic/leavematchbutton.qc:14
msgid "Stop demo"
-msgstr "Demo stoppen"
+msgstr "Wiederholung stoppen"
#: qcsrc/menu/xonotic/leavematchbutton.qc:16
msgid "Leave campaign"
#: qcsrc/common/monsters/monster/shambler.qh:17
#: qcsrc/menu/xonotic/dialog_monstertools.qc:17
msgid "Golem"
-msgstr ""
+msgstr "Golem"
#: qcsrc/common/monsters/monster/spider.qh:17
#: qcsrc/menu/xonotic/dialog_monstertools.qc:16
#: qcsrc/common/notifications/all.inc:303
#, c-format
msgid "^BG%s^K1's innards became outwards by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1s Eingeweide wurden von einem Golem%s%s nach aussen gekehrt"
#: qcsrc/common/notifications/all.inc:304
#, c-format
msgid "^BG%s^K1 was smashed by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1 wurde von einem Golem zerquetscht%s%s"
#: qcsrc/common/notifications/all.inc:305
#, c-format
msgid "^BG%s^K1 was zapped to death by a Golem%s%s"
-msgstr ""
+msgstr "^BG%s^K1 wurde von einem Golem%s%s zu Tode geschockt"
#: qcsrc/common/notifications/all.inc:306
#, c-format
#: qcsrc/menu/xonotic/leavematchbutton.qc:14
msgid "Stop demo"
-msgstr "Demo stoppen"
+msgstr "Wiederholung stoppen"
#: qcsrc/menu/xonotic/leavematchbutton.qc:16
msgid "Leave campaign"
#: qcsrc/common/notifications/all.inc:303
#, c-format
msgid "^BG%s^K1's innards became outwards by a Golem%s%s"
-msgstr "SaÃram de ^BG%s^K1 as entranhas por causa de um Golem%s%s"
+msgstr "^K1SaÃram de ^BG%s^K1 as entranhas por causa de um Golem%s%s"
#: qcsrc/common/notifications/all.inc:304
#, c-format
# Dmitro Sokhin <gamebot@ex.ua>, 2015
# Basil <cigariscigar@gmail.com>, 2011,2013
# Ihor Andreev, 2021
-# Ihor Andreev, 2021
+# Ihor Andreev <ihor@noleron.com>, 2021
+# Ihor Andreev <ihor@noleron.com>, 2021
# Dmitro Sokhin <gamebot@ex.ua>, 2015
msgid ""
msgstr ""
alias cl_hook_gamestart_ft
alias cl_hook_gamestart_inv
alias cl_hook_gamestart_duel
-alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends
+alias cl_hook_gameend
alias cl_hook_shutdown
alias cl_hook_activeweapon
set g_invasion_zombies_only 0 "only spawn zombies"
set g_invasion_spawn_delay 0.25
set g_invasion_spawnpoint_spawn_delay 0.5
-set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
-set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)"
// ======
ast "Asturian" "Asturianu" 75%
-de "German" "Deutsch" 99%
-de_CH "German (Switzerland)" "Deutsch (Schweiz)" 99%
+de "German" "Deutsch" 100%
+de_CH "German (Switzerland)" "Deutsch (Schweiz)" 100%
en "English" "English" 100%
en_AU "English (Australia)" "English (Australia)" 71%
es "Spanish" "Español" 100%
+++ /dev/null
-Plane01,arc_projectile_core
-Plane02,arc_projectile_long
--- /dev/null
+/*
+Generated framegroups file for golem
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 21 5 1 // golem idle
+22 61 30 1 // golem run
+83 21 30 1 // golem runangry
+104 36 30 1 // golem melee01
+140 36 30 1 // golem melee02
+176 61 30 1 // golem melee03
+237 61 30 1 // golem melee04
+298 21 30 0 // golem hit01
+319 21 30 0 // golem hit02
+340 21 30 0 // golem hit03
+361 21 30 0 // golem hit04
+382 61 30 0 // golem hithard
+443 101 30 0 // golem enrage
+544 61 30 0 // golem death01
+605 2 30 0 // golem dead01
+607 36 30 0 // golem death02
+643 2 30 0 // golem dead01
+645 46 30 0 // golem deathback
+691 2 30 0 // golem deadback
+693 2 30 0 // golem dead01
+695 2 30 0 // golem dead01
+697 2 30 0 // golem dead01
+699 2 30 0 // golem dead01
+701 2 30 0 // golem dead01
--- /dev/null
+//TAG: golem
+death sound/monsters/golem/death 3
+sight sound/monsters/golem/sight 0
+//ranged sound/monsters/golem/ranged 0
+melee sound/monsters/golem/melee 0
+pain sound/monsters/golem/pain 2
+//spawn sound/monsters/golem/spawn 0
+idle sound/monsters/golem/idle 2
+++ /dev/null
-/*
-Generated framegroups file for mage
-Used by DarkPlaces to simulate frame groups in DPM models.
-*/
-
-1 31 30 1 // mage idle
-32 31 30 1 // mage walk
-63 16 30 1 // mage attack
-79 16 30 1 // mage hit
-95 31 30 0 // mage die
-126 31 60 1 // mage walk
--- /dev/null
+/*
+Generated framegroups file for nanomage
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 61 12 1 // nanomage idle
+62 9 10 1 // nanomage run
+71 71 30 0 // nanomage attack01
+142 43 30 0 // nanomage attack02
+185 61 30 0 // nanomage attack03
+246 101 30 0 // nanomage attack04
+347 11 30 0 // nanomage pain01
+358 16 30 0 // nanomage pain02
+374 31 30 0 // nanomage pain03
+405 86 30 0 // nanomage death01
+491 56 30 0 // nanomage death02
+547 2 1 0 // nanomage dead01
+549 2 1 0 // nanomage dead02
+++ /dev/null
-1 16 10 1 // shambler stand\r18 11 10 1 // shambler walk\r31 5 10 1 // shambler run\r37 11 10 1 // shambler smash\r49 8 10 1 // shambler swing right\r58 8 10 1 // shambler swing left\r67 11 10 1 // shambler magic\r79 5 10 0 // shambler pain\r85 10 10 0 // shambler death
\ No newline at end of file
+++ /dev/null
-//TAG: shambler
-//death sound/monsters/shambler/death 0
-sight sound/monsters/shambler/sight 0
-//ranged sound/monsters/shambler/ranged 0
-//melee sound/monsters/shambler/melee 0
-//pain sound/monsters/shambler/pain 0
-//spawn sound/monsters/shambler/spawn 0
-idle sound/monsters/shambler/idle 2
Used by DarkPlaces to simulate frame groups in DPM models.
*/
-1 60 60 1 // spider idle
-61 41 120 1 // spider walk
-102 24 60 1 // spider attack
-125 10 60 1 // spider attack2
\ No newline at end of file
+1 51 30 0 // spider spiderbite
+52 51 30 0 // spider spiderdeath01
+103 51 30 0 // spider spiderdeath02
+154 11 30 0 // spider spiderfire01
+165 31 30 0 // spider spiderfire02
+196 51 5 1 // spider spideridle
+247 51 30 0 // spider spiderintimidate
+298 11 25 0 // spider spiderpain01
+309 11 25 0 // spider spiderpain02
+320 11 15 0 // spider spiderpain03
+331 101 30 1 // spider spiderwalkforward
+432 101 30 1 // spider spiderwalkforwardright
+533 101 30 1 // spider spiderwalkright
+634 101 30 1 // spider spiderwalkbackright
+735 101 30 1 // spider spiderwalkback
+836 101 30 1 // spider spiderwalkbackleft
+937 101 30 1 // spider spiderwalkleft
+1038 101 30 1 // spider spiderwalkforwardleft
+++ /dev/null
-1 14 10 1 // wizard hover\r16 13 10 1 // wizard fly\r30 12 10 1 // wizard magic attack\r43 3 10 0 // wizard pain\r47 7 10 0 // wizard death
\ No newline at end of file
--- /dev/null
+/*
+Generated framegroups file for wyvern
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 101 30 1 // wyvern idle
+102 101 30 1 // wyvern glide
+203 101 30 1 // wyvern fly
+304 26 30 0 // wyvern pain1
+330 26 30 0 // wyvern pain2
+356 51 30 0 // wyvern melee
+407 51 30 0 // wyvern fireball
+458 101 30 1 // wyvern dying
+559 26 30 0 // wyvern dead
-erebus,erebus
-erebus.001,shadowhead
+erebus,erebusfullbright
+erebus.001,shadowheadfb
-erebus,erebusfullbright
+erebus,erebus
erebus.001,shadowhead
-erebus,erebus
-erebus.001,shadowhead
+erebus,erebusfullbright
+erebus.001,shadowheadfb
-erebus,erebusfullbright
+erebus,erebus
erebus.001,shadowhead
-erebus,erebus
-erebus.001,shadowhead
+erebus,erebusfullbright
+erebus.001,shadowheadfb
-erebus,erebusfullbright
+erebus,erebus
erebus.001,shadowhead
-gak2.001,gakarmor
-gak2,gak
+gak2.001,gakarmorfb
+gak2,gakfullbright
-gak2.001,gakarmorfb
-gak2,gakfullbright
+gak2.001,gakarmor
+gak2,gak
-gak2.001,gakarmor
-gak2,gak
+gak2.001,gakarmorfb
+gak2,gakfullbright
-gak2.001,gakarmorfb
-gak2,gakfullbright
+gak2.001,gakarmor
+gak2,gak
-gak2.001,gakarmor
-gak2,gak
+gak2.001,gakarmorfb
+gak2,gakfullbright
-gak2.001,gakarmorfb
-gak2,gakfullbright
+gak2.001,gakarmor
+gak2,gak
-gak3,gak
-gak3.001,gakarmor
+gak3,gakfullbright
+gak3.001,gakarmorfb
-gak3,gakfullbright
-gak3.001,gakarmorfb
+gak3,gak
+gak3.001,gakarmor
-gak3,gak
-gak3.001,gakarmor
+gak3,gakfullbright
+gak3.001,gakarmorfb
-gak3,gakfullbright
-gak3.001,gakarmorfb
+gak3,gak
+gak3.001,gakarmor
-gak3,gak
-gak3.001,gakarmor
+gak3,gakfullbright
+gak3.001,gakarmorfb
-gak3,gakfullbright
-gak3.001,gakarmorfb
+gak3,gak
+gak3.001,gakarmor
-ignis1,ignis
-ignis1.001,ignishead
+ignis1,ignisfullbright
+ignis1.001,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis1.001,ignishead
-ignis1,ignis
-ignis1.001,ignishead
+ignis1,ignisfullbright
+ignis1.001,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis1.001,ignishead
-ignis1,ignis
-ignis1.001,ignishead
+ignis1,ignisfullbright
+ignis1.001,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis1.001,ignishead
-ignis1,ignis
-ignis3,ignishead
+ignis1,ignisfullbright
+ignis3,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis3,ignishead
-ignis1,ignis
-ignis3,ignishead
+ignis1,ignisfullbright
+ignis3,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis3,ignishead
-ignis1,ignis
-ignis3,ignishead
+ignis1,ignisfullbright
+ignis3,ignisheadfb
-ignis1,ignisfullbright
+ignis1,ignis
ignis3,ignishead
-erebus,erebusfullbright
-erebus.001,shadowhead
+erebus,megaerebus
+erebus.001,shadowheadfb
-erebus,erebusfullbright
-erebus.001,shadowhead
+erebus,megaerebus
+erebus.001,shadowheadfb
-erebus,erebusfullbright
-erebus.001,shadowhead
+erebus,megaerebus
+erebus.001,shadowheadfb
-nyx,nyx
-nyx.001,shadowhead
+nyx,nyxfullbright
+nyx.001,shadowheadfb
-nyx,nyxfullbright
+nyx,nyx
nyx.001,shadowhead
-nyx,nyx
-nyx.001,shadowhead
+nyx,nyxfullbright
+nyx.001,shadowheadfb
-nyx,nyxfullbright
+nyx,nyx
nyx.001,shadowhead
-nyx,nyx
-nyx.001,shadowhead
+nyx,nyxfullbright
+nyx.001,shadowheadfb
-nyx,nyxfullbright
+nyx,nyx
nyx.001,shadowhead
pyria_obj.001,pyriahair
-pyria_obj,pyria
+pyria_obj,pyriafullbright
pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
pyria_obj.001,pyriahair
-pyria_obj,pyria
+pyria_obj,pyriafullbright
pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
pyria_obj.001,pyriahair
-pyria_obj,pyria
+pyria_obj,pyriafullbright
pyria_obj.001,pyriahair
-pyria_obj,pyriafullbright
+pyria_obj,pyria
ignis42.001,seraphina
-ignis42,ignis
+ignis42,ignisfullbright
ignis42.001,seraphina
-ignis42,ignisfullbright
+ignis42,ignis
ignis42.001,seraphina
-ignis42,ignis
+ignis42,ignisfullbright
ignis42.001,seraphina
-ignis42,ignisfullbright
+ignis42,ignis
ignis42.001,seraphina
-ignis42,ignis
+ignis42,ignisfullbright
ignis42.001,seraphina
-ignis42,ignisfullbright
+ignis42,ignis
-ignis42,ignis
-ignis42.002,ignishead
+ignis42,ignisfullbright
+ignis42.002,ignisheadfb
-ignis42,ignisfullbright
+ignis42,ignis
ignis42.002,ignishead
-ignis42,ignis
-ignis42.002,ignishead
+ignis42,ignisfullbright
+ignis42.002,ignisheadfb
-ignis42,ignisfullbright
+ignis42,ignis
ignis42.002,ignishead
-ignis42,ignis
-ignis42.002,ignishead
+ignis42,ignisfullbright
+ignis42.002,ignisheadfb
-ignis42,ignisfullbright
+ignis42,ignis
ignis42.002,ignishead
-umbra,umbra
+umbra,umbrafullbright
-umbra,umbrafullbright
+umbra,umbra
-umbra,umbra
+umbra,umbrafullbright
-umbra,umbrafullbright
+umbra,umbra
-umbra,umbra
+umbra,umbrafullbright
-umbra,umbrafullbright
+umbra,umbra
set g_monster_zombie_attack_leap_speed 500
set g_monster_zombie_attack_melee_damage 55
set g_monster_zombie_attack_melee_delay 1
-set g_monster_zombie_damageforcescale 0.550000012
+set g_monster_zombie_damageforcescale 0.55
set g_monster_zombie_health 200
set g_monster_zombie_speed_run 600
set g_monster_zombie_speed_stop 100
set g_monster_spider_attack_bite_damage 35
set g_monster_spider_attack_bite_delay 1.5
set g_monster_spider_attack_web_damagetime 7
-set g_monster_spider_attack_web_delay 1.5
+set g_monster_spider_attack_web_delay 3
+set g_monster_spider_attack_web_range 800
set g_monster_spider_attack_web_speed 1300
set g_monster_spider_attack_web_speed_up 150
-set g_monster_spider_damageforcescale 0.600000024
+set g_monster_spider_damageforcescale 0.6
set g_monster_spider_health 180
set g_monster_spider_speed_run 500
set g_monster_spider_speed_stop 100
set g_monster_spider_speed_walk 400
// }}}
// {{{ #3: Mage
+set g_monster_mage_attack_push_chance 0.7
set g_monster_mage_attack_push_damage 25
set g_monster_mage_attack_push_delay 1
set g_monster_mage_attack_push_force 300
set g_monster_mage_attack_push_radius 150
set g_monster_mage_attack_spike_accel 480
+set g_monster_mage_attack_spike_chance 0.45
set g_monster_mage_attack_spike_damage 45
set g_monster_mage_attack_spike_decel 480
set g_monster_mage_attack_spike_delay 2
set g_monster_mage_attack_spike_smart_trace_min 1000
set g_monster_mage_attack_spike_speed_max 370
set g_monster_mage_attack_spike_turnrate 0.65
+set g_monster_mage_attack_teleport_chance 0.1
+set g_monster_mage_attack_teleport_delay 5
+set g_monster_mage_attack_teleport_random 0.4
+set g_monster_mage_attack_teleport_random_range 1200
set g_monster_mage_damageforcescale 0.5
set g_monster_mage_heal_allies 20
set g_monster_mage_heal_delay 1.5
set g_monster_wyvern_attack_fireball_force 50
set g_monster_wyvern_attack_fireball_radius 120
set g_monster_wyvern_attack_fireball_speed 1200
-set g_monster_wyvern_damageforcescale 0.600000024
+set g_monster_wyvern_damageforcescale 0.6
set g_monster_wyvern_health 150
set g_monster_wyvern_speed_run 250
set g_monster_wyvern_speed_stop 300
set g_monster_wyvern_speed_walk 120
// }}}
-// {{{ #5: Shambler
-set g_monster_shambler_attack_claw_damage 60
-set g_monster_shambler_attack_lightning_damage 25
-set g_monster_shambler_attack_lightning_damage_zap 15
-set g_monster_shambler_attack_lightning_force 100
-set g_monster_shambler_attack_lightning_radius 50
-set g_monster_shambler_attack_lightning_radius_zap 250
-set g_monster_shambler_attack_lightning_speed 1000
-set g_monster_shambler_attack_lightning_speed_up 150
-set g_monster_shambler_attack_smash_damage 50
-set g_monster_shambler_attack_smash_range 0
-set g_monster_shambler_damageforcescale 0.100000001
-set g_monster_shambler_health 650
-set g_monster_shambler_speed_run 320
-set g_monster_shambler_speed_stop 300
-set g_monster_shambler_speed_walk 150
+// {{{ #5: Golem
+set g_monster_golem_attack_claw_damage 60
+set g_monster_golem_attack_lightning_damage 25
+set g_monster_golem_attack_lightning_damage_zap 15
+set g_monster_golem_attack_lightning_force 100
+set g_monster_golem_attack_lightning_radius 50
+set g_monster_golem_attack_lightning_radius_zap 250
+set g_monster_golem_attack_lightning_speed 1000
+set g_monster_golem_attack_lightning_speed_up 150
+set g_monster_golem_attack_smash_damage 50
+set g_monster_golem_attack_smash_force 100
+set g_monster_golem_attack_smash_range 200
+set g_monster_golem_damageforcescale 0.1
+set g_monster_golem_health 650
+set g_monster_golem_speed_run 320
+set g_monster_golem_speed_stop 300
+set g_monster_golem_speed_walk 150
// }}}
// {{{ Misc
set g_monsters_ignoretraces 1
set g_monsters_lineofsight 1
set g_monsters_owners 1
-set g_monsters_teams 1
+set g_monsters_playerclip_collisions 1
set g_monsters_score_kill 0
set g_monsters_score_spawned 0
set g_monsters_sounds 1
set g_monsters_target_range 2000
set g_monsters_target_infront 0
set g_monsters_target_infront_range 0.3
+set g_monsters_target_infront_2d 1
set g_monsters_attack_range 120
set g_monsters_respawn 1
set g_monsters_respawn_delay 20
seta notification_INFO_DEATH_SELF_FIRE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_GENERIC "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_LAVA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_DEATH_SELF_MON_GOLEM_CLAW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_DEATH_SELF_MON_GOLEM_SMASH "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
+seta notification_INFO_DEATH_SELF_MON_GOLEM_ZAP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_MON_MAGE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-seta notification_INFO_DEATH_SELF_MON_SHAMBLER_CLAW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-seta notification_INFO_DEATH_SELF_MON_SHAMBLER_SMASH "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-seta notification_INFO_DEATH_SELF_MON_SHAMBLER_ZAP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_MON_SPIDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_MON_WYVERN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_INFO_DEATH_SELF_MON_ZOMBIE_JUMP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
seta notification_DEATH_SELF_FIRE "1" "Enable this multiple notification"
seta notification_DEATH_SELF_GENERIC "1" "Enable this multiple notification"
seta notification_DEATH_SELF_LAVA "1" "Enable this multiple notification"
+seta notification_DEATH_SELF_MON_GOLEM_CLAW "1" "Enable this multiple notification"
+seta notification_DEATH_SELF_MON_GOLEM_SMASH "1" "Enable this multiple notification"
+seta notification_DEATH_SELF_MON_GOLEM_ZAP "1" "Enable this multiple notification"
seta notification_DEATH_SELF_MON_MAGE "1" "Enable this multiple notification"
-seta notification_DEATH_SELF_MON_SHAMBLER_CLAW "1" "Enable this multiple notification"
-seta notification_DEATH_SELF_MON_SHAMBLER_SMASH "1" "Enable this multiple notification"
-seta notification_DEATH_SELF_MON_SHAMBLER_ZAP "1" "Enable this multiple notification"
seta notification_DEATH_SELF_MON_SPIDER "1" "Enable this multiple notification"
seta notification_DEATH_SELF_MON_WYVERN "1" "Enable this multiple notification"
seta notification_DEATH_SELF_MON_ZOMBIE_JUMP "1" "Enable this multiple notification"
strcpy(prev_pl2_name, pl2_name);
// There are new duelers, update title
- centerprint_SetDuelTitle(pl1_name, pl2_name, _("vs"));
+ centerprint_SetDuelTitle(pl1_name, pl2_name);
}
void Announcer_ClearTitle()
.int lodmodelindex0;
.int lodmodelindex1;
.int lodmodelindex2;
-void CSQCPlayer_LOD_Apply(entity this)
+void CSQCPlayer_LOD_Apply(entity this, bool isplayer)
{
+ int detailreduction = ((isplayer) ? autocvar_cl_playerdetailreduction : autocvar_cl_modeldetailreduction);
+
// LOD model loading
if(this.lodmodelindex0 != this.modelindex)
{
}
// apply LOD
- if(autocvar_cl_playerdetailreduction <= 0)
+ if(detailreduction <= 0)
{
- if(autocvar_cl_playerdetailreduction <= -2)
+ if(detailreduction <= -2)
this.modelindex = this.lodmodelindex2;
- else if(autocvar_cl_playerdetailreduction <= -1)
+ else if(detailreduction <= -1)
this.modelindex = this.lodmodelindex1;
else
this.modelindex = this.lodmodelindex0;
}
else
{
- float distance = vlen(this.origin - view_origin);
- float f = (distance * current_viewzoom + 100.0) * autocvar_cl_playerdetailreduction;
+ float distance = vlen(((isplayer) ? this.origin : NearestPointOnBox(this, view_origin)) - view_origin); // TODO: perhaps it should just use NearestPointOnBox all the time, player hitbox can potentially be huge
+ float f = (distance * current_viewzoom + 100.0) * detailreduction;
f *= 1.0 / bound(0.01, view_quality, 1);
if(f > autocvar_cl_loddistance2)
this.modelindex = this.lodmodelindex2;
// GLOWMOD AND DEATH FADING
if(this.colormap > 0)
- this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true) * 2;
+ this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true);
else
this.glowmod = '1 1 1';
}
}
+ // don't let the engine increase player's glowmod
+ if (autocvar_r_hdr_glowintensity > 1)
+ this.glowmod /= autocvar_r_hdr_glowintensity;
+
//printf("CSQCPlayer_ModelAppearance_Apply(): state = %s, colormap = %f, glowmod = %s\n", (this.csqcmodel_isdead ? "DEAD" : "ALIVE"), this.colormap, vtos(this.glowmod));
}
if((this.isplayermodel & ISPLAYER_MODEL) && this.drawmask) // this checks if it's a player MODEL!
{
CSQCPlayer_ModelAppearance_Apply(this, (this.isplayermodel & ISPLAYER_LOCAL));
- CSQCPlayer_LOD_Apply(this);
+ CSQCPlayer_LOD_Apply(this, true);
if(!isplayer)
{
}
}
}
+ else
+ CSQCPlayer_LOD_Apply(this, false);
CSQCModel_AutoTagIndex_Apply(this);
float autocvar_cl_deathglow;
float autocvar_cl_deathglow_min = 0.5;
float autocvar_cl_jetpack_attenuation = 2;
+float autocvar_r_hdr_glowintensity;
// FEATURE: EF_NODRAW workalike
const int EF_BRIGHTFIELD = BIT(0);
}
}
-void centerprint_SetDuelTitle(string left, string right, string div)
+void centerprint_SetDuelTitle(string left, string right)
{
- strcpy(centerprint_title_left, left);
- strcpy(centerprint_title_right, right);
- centerprint_SetTitle(sprintf("^BG%s^BG %s %s", left, div, right));
+ float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
+ strcpy(centerprint_title_left, textShortenToWidth(left, namesize, hud_fontsize, stringwidth_colors));
+ strcpy(centerprint_title_right, textShortenToWidth(right, namesize, hud_fontsize, stringwidth_colors));
}
void centerprint_SetTitle(string title)
align = bound(0, autocvar_hud_panel_centerprint_align, 1);
// Show title if available
- if(centerprint_title) {
+ if(centerprint_title != "" || centerprint_title_left != "") {
vector fontsize = cp_fontsize * autocvar_hud_panel_centerprint_fontscale_title;
- float width = stringwidth(centerprint_title, true, fontsize);
-
- pos.x = panel_pos.x + (panel_size.x - width) * align;
+ float width = 0, right_width = 0, left_width = 0, max_rl_width = 0;
+ if (centerprint_title != "")
+ {
+ width = stringwidth(centerprint_title, true, fontsize);
+ }
+ else
+ {
+ right_width = stringwidth(centerprint_title_right, true, fontsize);
+ left_width = stringwidth(centerprint_title_left, true, fontsize);
+ }
if (autocvar_hud_panel_centerprint_flip)
pos.y -= fontsize.y;
- if (centerprint_title_left != "" && align == 0.5) // Center line at the main word (for duels)
- pos.x += (stringwidth(centerprint_title_right, true, fontsize) - stringwidth(centerprint_title_left, true, fontsize)) / 2;
- drawcolorcodedstring(pos, centerprint_title, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ vector duel_title_pos = '0 0 0';
+ float padding = stringwidth(" ", false, fontsize) * 2;
+ if (centerprint_title_left != "")
+ {
+ if (left_width > right_width)
+ max_rl_width = left_width;
+ else
+ max_rl_width = right_width;
+
+ width = max_rl_width * 2 + padding * 6 + stringwidth(_("vs"), false, fontsize);
+ pos.x += (panel_size.x - width) * align;
+ duel_title_pos = pos;
+
+ pos.x += padding;
+ if (left_width < right_width)
+ pos.x += (max_rl_width - left_width) * 0.5;
+ drawcolorcodedstring(pos, centerprint_title_left, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ pos.x = duel_title_pos.x + max_rl_width + padding * 3;
+ drawstring(pos, _("vs"), fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ pos.x = duel_title_pos.x + width - padding - max_rl_width;
+ if (left_width >= right_width)
+ pos.x += (max_rl_width - right_width) * 0.5;
+ drawcolorcodedstring(pos, centerprint_title_right, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ {
+ pos.x = panel_pos.x + (panel_size.x - width) * align;
+ drawcolorcodedstring(pos, centerprint_title, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
if (autocvar_hud_panel_centerprint_flip)
pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
else
pos.y += fontsize.y + (hud_fontsize.y * CENTERPRINT_TITLE_SPACING);
- drawfill(pos, vec2(width, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ if (centerprint_title_left != "")
+ {
+ pos.x = duel_title_pos.x;
+ drawfill(pos, vec2(max_rl_width + padding * 2, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawfill(pos + vec2(width - max_rl_width - padding * 2, 0), vec2(max_rl_width + padding * 2, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ drawfill(pos, vec2(width, 1), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
if (autocvar_hud_panel_centerprint_flip)
pos.y -= cp_fontsize.y * CENTERPRINT_TITLE_SPACING;
void centerprint_Kill(int id);
void centerprint_KillAll();
-void centerprint_SetDuelTitle(string left, string right, string div);
+void centerprint_SetDuelTitle(string left, string right);
void centerprint_SetTitle(string title);
void centerprint_ClearTitle();
float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated = 0.6;
float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
-float autocvar_hud_panel_scoreboard_namesize = 15;
float autocvar_hud_panel_scoreboard_team_size_position = 0;
float autocvar_hud_panel_scoreboard_spectators_position = 1;
bool autocvar_cl_deathscoreboard;
string autocvar_scoreboard_columns;
+float autocvar_hud_panel_scoreboard_namesize = 15;
bool scoreboard_showscores;
if (!(calledhooks & HOOK_START))
localcmd("\n_cl_hook_gamestart nop\n");
if (!(calledhooks & HOOK_END))
+ {
+ int gamecount = cvar("cl_matchcount");
localcmd("\ncl_hook_gameend\n");
+ // NOTE: using localcmd here to ensure it's executed AFTER cl_hook_gameend
+ // earlier versions of the game abuse the hook to set this cvar
+ localcmd(strcat("cl_matchcount ", itos(gamecount + 1), "\n"));
+ //cvar_set("cl_matchcount", itos(gamecount + 1));
+ }
}
localcmd("\ncl_hook_shutdown\n");
float current_zoomfraction;
-int cs_project_is_b0rked;
int vid_width, vid_height;
float vid_pixelheight;
first_digit = 0;
}
+ // Key release events must be handled by the engine otherwise the on-press command such as +jump
+ // executed by pressing SPACE before entering the map voting screen won't be followed by the
+ // on-release command (-jump) on key release once entered the map voting screen, causing +jump
+ // to stay active even on the next map and automatically forcing the player to join
+ if (!key_pressed) return false;
+
int imp = 0;
switch(nPrimary)
{
case K_RIGHTARROW:
- if (!key_pressed) return true;
mv_selection_keyboard = 1;
mv_selection = MapVote_MoveRight(mv_selection);
return true;
case K_LEFTARROW:
- if (!key_pressed) return true;
mv_selection_keyboard = 1;
mv_selection = MapVote_MoveLeft(mv_selection);
return true;
case K_DOWNARROW:
- if (!key_pressed) return true;
mv_selection_keyboard = 1;
mv_selection = MapVote_MoveDown(mv_selection);
return true;
case K_UPARROW:
- if (!key_pressed) return true;
mv_selection_keyboard = 1;
mv_selection = MapVote_MoveUp(mv_selection);
return true;
case K_KP_ENTER:
case K_ENTER:
case K_SPACE:
- if (!key_pressed) return true;
if ( mv_selection_keyboard )
MapVote_SendChoice(mv_selection);
return true;
{
if (!first_digit)
{
- if (!key_pressed)
- return true;
first_digit = imp % 10;
return true;
}
if (nPrimary == K_MOUSE1)
{
- if (!key_pressed)
- return true;
mv_selection_keyboard = 0;
mv_selection = mv_mouse_selection;
if (mv_selection >= 0)
if (imp)
{
- if (!key_pressed)
- return true;
if (imp <= mv_num_maps)
localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
return true;
vector project_3d_to_2d(vector vec)
{
vec = cs_project(vec);
- if(cs_project_is_b0rked > 0)
- {
- vec.x *= vid_conwidth / vid_width;
- vec.y *= vid_conheight / vid_height;
- }
return vec;
}
{
if(calledhooks & HOOK_START)
{
+ int gamecount = cvar("cl_matchcount");
localcmd("\ncl_hook_gameend\n");
+ // NOTE: using localcmd here to ensure it's executed AFTER cl_hook_gameend
+ // earlier versions of the game abuse the hook to set this cvar
+ localcmd(strcat("cl_matchcount ", itos(gamecount + 1), "\n"));
+ //cvar_set("cl_matchcount", itos(gamecount + 1));
calledhooks |= HOOK_END;
}
}
NextFrameCommand = string_null;
}
- // we must do this check AFTER a frame was rendered, or it won't work
- if(cs_project_is_b0rked == 0)
- {
- string w0, h0;
- w0 = ftos(autocvar_vid_conwidth);
- h0 = ftos(autocvar_vid_conheight);
- //setproperty(VF_VIEWPORT, '0 0 0', '640 480 0');
- //setproperty(VF_FOV, '90 90 0');
- setproperty(VF_ORIGIN, '0 0 0');
- setproperty(VF_ANGLES, '0 0 0');
- setproperty(VF_PERSPECTIVE, 1);
- vector forward, right, up;
- MAKE_VECTORS('0 0 0', forward, right, up);
- vector v1, v2;
- cvar_set("vid_conwidth", "800");
- cvar_set("vid_conheight", "600");
- v1 = cs_project(forward);
- cvar_set("vid_conwidth", "640");
- cvar_set("vid_conheight", "480");
- v2 = cs_project(forward);
- if(v1 == v2)
- cs_project_is_b0rked = 1;
- else
- cs_project_is_b0rked = -1;
- cvar_set("vid_conwidth", w0);
- cvar_set("vid_conheight", h0);
- }
-
HUD_Mouse(local_player);
cl_notice_run();
// TODO: projectiles use glowmaps for their color, not teams
#if 0
if(this.colormap > 0)
- this.glowmod = colormapPaletteColor(this.colormap & 0x0F, true) * 2;
+ this.glowmod = colormapPaletteColor(this.colormap & 0x0F, true);
else
this.glowmod = '1 1 1';
#endif
HANDLE(SEEKER) this.traileffect = EFFECT_SEEKER_TRAIL.m_id; break;
HANDLE(MAGE_SPIKE) this.traileffect = EFFECT_TR_VORESPIKE.m_id; break;
- HANDLE(SHAMBLER_LIGHTNING) this.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+ HANDLE(GOLEM_LIGHTNING) this.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
HANDLE(RAPTORBOMB) this.gravity = 1; this.avelocity = '0 0 180'; this.traileffect = EFFECT_Null.m_id; break;
HANDLE(RAPTORBOMBLET) this.gravity = 1; this.avelocity = '0 0 180'; this.traileffect = EFFECT_Null.m_id; break;
this.bouncefactor = WEP_CVAR(mortar, bouncefactor);
this.bouncestop = WEP_CVAR(mortar, bouncestop);
break;
- case PROJECTILE_SHAMBLER_LIGHTNING:
+ case PROJECTILE_GOLEM_LIGHTNING:
this.mins = '-8 -8 -8';
this.maxs = '8 8 8';
this.scale = 2.5;
REGISTER_DEATHTYPE(LAVA, DEATH_SELF_LAVA, DEATH_MURDER_LAVA, "")
REGISTER_DEATHTYPE(MIRRORDAMAGE, DEATH_SELF_BETRAYAL, NULL, "")
REGISTER_DEATHTYPE(MONSTER_MAGE, DEATH_SELF_MON_MAGE, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_CLAW, DEATH_SELF_MON_SHAMBLER_CLAW, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_SMASH, DEATH_SELF_MON_SHAMBLER_SMASH, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_ZAP, DEATH_SELF_MON_SHAMBLER_ZAP, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_CLAW, DEATH_SELF_MON_GOLEM_CLAW, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_SMASH, DEATH_SELF_MON_GOLEM_SMASH, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_ZAP, DEATH_SELF_MON_GOLEM_ZAP, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_SPIDER, DEATH_SELF_MON_SPIDER, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_WYVERN, DEATH_SELF_MON_WYVERN, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_ZOMBIE_JUMP, DEATH_SELF_MON_ZOMBIE_JUMP, DEATH_MURDER_MONSTER, "monster")
CLASS(Invasion, Gametype)
INIT(Invasion)
{
- this.gametype_init(this, _("Invasion"),"inv","g_invasion",GAMETYPE_FLAG_USEPOINTS,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters"));
+ this.gametype_init(this, _("Invasion"),"inv","g_invasion",GAMETYPE_FLAG_USEPOINTS,"","pointlimit=50 type=0",_("Survive against waves of monsters"));
}
METHOD(Invasion, m_parse_mapinfo, bool(string k, string v))
{
switch (k) {
- case "teams":
- cvar_set("g_invasion_teams", v);
- return true;
case "type":
cvar_set("g_invasion_type", v);
return true;
if(it.winning)
{
bprint("Invasion: round completed.\n");
- // winners already set (TODO: teamplay support)
+ // winners already set
status = WINNING_YES;
break;
FOREACH(Monsters, it != MON_Null,
{
- if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM) ||
- (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
+ if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM)
+ || (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
continue;
if(autocvar_g_invasion_zombies_only && !(it.spawnflags & MONSTER_TYPE_UNDEAD))
continue;
setsize(e, mon.m_mins, mon.m_maxs);
if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
+ {
monster = spawnmonster(e, tospawn, mon, NULL, NULL, e.origin, false, false, 2);
+ monster.angles_x = monster.angles_z = 0;
+ }
else
{
delete(e);
monster.target2 = spawn_point.target2;
}
- if(teamplay)
- {
- if(spawn_point && spawn_point.team && inv_monsters_perteam[spawn_point.team] > 0)
- monster.team = spawn_point.team;
- else
- {
- RandomSelection_Init();
- if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_AddFloat(NUM_TEAM_1, 1, 1);
- if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_AddFloat(NUM_TEAM_2, 1, 1);
- if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_AddFloat(NUM_TEAM_3, 1, 1); }
- if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_AddFloat(NUM_TEAM_4, 1, 1); }
-
- monster.team = RandomSelection_chosen_float;
- }
-
- monster_setupcolors(monster);
-
- if(monster.sprite)
- {
- WaypointSprite_UpdateTeamRadar(monster.sprite, RADARICON_DANGER, ((monster.team) ? Team_ColorRGB(monster.team) : '1 0 0'));
-
- monster.sprite.team = 0;
- monster.sprite.SendFlags |= 1;
- }
- }
-
if(monster.monster_attack)
IL_REMOVE(g_monster_targets, monster);
monster.monster_attack = false; // it's the player's job to kill all the monsters
return 1;
}
- float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0;
+ float total_alive_monsters = 0, supermonster_count = 0;
IL_EACH(g_monsters, GetResource(it, RES_HEALTH) > 0,
{
if(it.monsterdef.spawnflags & MON_FLAG_SUPERMONSTER)
++supermonster_count;
++total_alive_monsters;
-
- if(teamplay)
- switch(it.team)
- {
- case NUM_TEAM_1: ++red_alive; break;
- case NUM_TEAM_2: ++blue_alive; break;
- case NUM_TEAM_3: ++yellow_alive; break;
- case NUM_TEAM_4: ++pink_alive; break;
- }
});
if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned)
if(inv_numspawned < 1)
return 0; // nothing has spawned yet
- if(teamplay)
- {
- if(((red_alive > 0) + (blue_alive > 0) + (yellow_alive > 0) + (pink_alive > 0)) > 1)
- return 0;
- }
- else if(inv_numkilled < inv_maxspawned)
+ if(inv_numkilled < inv_maxspawned)
return 0;
entity winner = NULL;
- float winning_score = 0, winner_team = 0;
+ float winning_score = 0;
-
- if(teamplay)
- {
- if(red_alive > 0) { winner_team = NUM_TEAM_1; }
- if(blue_alive > 0)
- {
- if(winner_team) { winner_team = 0; }
- else { winner_team = NUM_TEAM_2; }
- }
- if(yellow_alive > 0)
- {
- if(winner_team) { winner_team = 0; }
- else { winner_team = NUM_TEAM_3; }
- }
- if(pink_alive > 0)
+ FOREACH_CLIENT(IS_PLAYER(it), {
+ float cs = GameRules_scoring_add(it, KILLS, 0);
+ if(cs > winning_score)
{
- if(winner_team) { winner_team = 0; }
- else { winner_team = NUM_TEAM_4; }
+ winning_score = cs;
+ winner = it;
}
- }
- else
- {
- FOREACH_CLIENT(IS_PLAYER(it), {
- float cs = GameRules_scoring_add(it, KILLS, 0);
- if(cs > winning_score)
- {
- winning_score = cs;
- winner = it;
- }
- });
- }
+ });
IL_EACH(g_monsters, true,
{
});
IL_CLEAR(g_monsters);
- if(teamplay)
- {
- if(winner_team)
- {
- Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
- }
- }
- else if(winner)
+ if(winner)
{
Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, winner.netname);
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_PLAYER_WIN, winner.netname);
inv_numkilled = 0;
inv_maxspawned = rint(max(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5)));
-
- if(teamplay)
- {
- DistributeEvenly_Init(inv_maxspawned, invasion_teams);
- inv_monsters_perteam[NUM_TEAM_1] = DistributeEvenly_Get(1);
- inv_monsters_perteam[NUM_TEAM_2] = DistributeEvenly_Get(1);
- if(invasion_teams >= 3) inv_monsters_perteam[NUM_TEAM_3] = DistributeEvenly_Get(1);
- if(invasion_teams >= 4) inv_monsters_perteam[NUM_TEAM_4] = DistributeEvenly_Get(1);
- }
}
MUTATOR_HOOKFUNCTION(inv, MonsterDies)
inv_numkilled += 1;
inv_maxcurrent -= 1;
}
- if(teamplay) { inv_monsters_perteam[frag_target.team] -= 1; }
if(IS_PLAYER(frag_attacker))
{
- if(SAME_TEAM(frag_attacker, frag_target)) // in non-teamplay modes, same team = same player, so this works
+ if(SAME_TEAM(frag_attacker, frag_target))
GameRules_scoring_add(frag_attacker, KILLS, -1);
else
- {
GameRules_scoring_add(frag_attacker, KILLS, +1);
- if(teamplay)
- TeamScore_AddToTeam(frag_attacker.team, ST_INV_KILLS, +1);
- }
}
}
}
return true;
}
-MUTATOR_HOOKFUNCTION(inv, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
-{
- M_ARGV(0, float) = invasion_teams;
- return true;
-}
-
MUTATOR_HOOKFUNCTION(inv, AllowMobButcher)
{
M_ARGV(0, string) = "This command does not work during an invasion!";
return true;
}
-void invasion_ScoreRules(int inv_teams)
+void invasion_ScoreRules()
{
GameRules_score_enabled(false);
- GameRules_scoring(inv_teams, 0, 0, {
- if (inv_teams) {
- field_team(ST_INV_KILLS, "frags", SFL_SORT_PRIO_PRIMARY);
- }
- field(SP_KILLS, "frags", ((inv_teams) ? SFL_SORT_PRIO_SECONDARY : SFL_SORT_PRIO_PRIMARY));
+ GameRules_scoring(0, 0, 0, {
+ field(SP_KILLS, "kills", SFL_SORT_PRIO_PRIMARY);
});
}
-void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
+void invasion_DelayedInit(entity this)
{
if(autocvar_g_invasion_type == INV_TYPE_HUNT || autocvar_g_invasion_type == INV_TYPE_STAGE)
cvar_set("fraglimit", "0");
- if(autocvar_g_invasion_teams)
- {
- invasion_teams = BITS(bound(2, autocvar_g_invasion_teams, 4));
- }
- else
- invasion_teams = 0;
-
independent_players = 1; // to disable extra useless scores
- invasion_ScoreRules(invasion_teams);
+ invasion_ScoreRules();
independent_players = 0;
#include <common/mutators/base.qh>
#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
-int autocvar_g_invasion_teams;
int autocvar_g_invasion_type;
-bool autocvar_g_invasion_team_spawns;
bool g_invasion;
IntrusiveList g_invasion_roundends;
IntrusiveList g_invasion_waves;
g_invasion_roundends = IL_NEW();
g_invasion_waves = IL_NEW();
g_invasion_spawns = IL_NEW();
- if (autocvar_g_invasion_teams >= 2) {
- GameRules_teams(true);
- GameRules_spawning_teams(autocvar_g_invasion_team_spawns);
- }
GameRules_limit_score(autocvar_g_invasion_point_limit);
g_invasion = true;
return 0;
}
-float inv_numspawned;
-float inv_maxspawned;
-float inv_roundcnt;
-float inv_maxrounds;
-float inv_numkilled;
+int inv_numspawned;
+int inv_maxspawned;
+int inv_roundcnt;
+int inv_maxrounds;
+int inv_numkilled;
float inv_lastcheck;
-float inv_maxcurrent;
-
-float invasion_teams;
-float inv_monsters_perteam[17];
+int inv_maxcurrent;
float inv_monsterskill;
-const float ST_INV_KILLS = 1;
-
const int INV_TYPE_ROUND = 0; // round-based waves of enemies
const int INV_TYPE_HUNT = 1; // clear the map of placed enemies
const int INV_TYPE_STAGE = 2; // reach the end of the level
this.takedamage = DAMAGE_NO;
this.event_damage = func_null;
this.bot_attack = false;
+ this.monster_attack = false;
}
// precache all the models
this.reset = func_breakable_reset;
this.reset(this);
+ if(this.monster_attack)
+ IL_PUSH(g_monster_targets, this);
+
IL_PUSH(g_initforplayer, this);
this.init_for_player = func_breakable_init_for_player;
MODEL(PROJECTILE_HAGAR, "models/hagarmissile.mdl");
MODEL(PROJECTILE_HAGAR_BOUNCING, "models/hagarmissile.mdl");
-MODEL(PROJECTILE_ARC_BOLT, "models/ebomb.mdl"); // TODO: remove models/arctrail.mdl right before 0.8.3 release!
+MODEL(PROJECTILE_ARC_BOLT, "models/ebomb.mdl");
// napalm grenade
MODEL(PROJECTILE_NAPALM_FOUNTAIN, "null");
MODEL(PROJECTILE_SEEKER, "models/tagrocket.md3");
MODEL(PROJECTILE_MAGE_SPIKE, "models/ebomb.mdl");
-MODEL(PROJECTILE_SHAMBLER_LIGHTNING, "models/ebomb.mdl");
+MODEL(PROJECTILE_GOLEM_LIGHTNING, "models/ebomb.mdl");
MODEL(PROJECTILE_RAPTORBOMB, "models/vehicles/clusterbomb.md3");
MODEL(PROJECTILE_RAPTORBOMBLET, "models/vehicles/bomblet.md3");
const int MONSTER_RESPAWN_DEATHPOINT = BIT(4); // re-spawn where we died
const int MONSTER_TYPE_FLY = BIT(5);
const int MONSTER_TYPE_SWIM = BIT(6);
-const int MONSTER_SIZE_BROKEN = BIT(7); // TODO: remove when bad models are replaced
+// bit 7 now unused
const int MON_FLAG_SUPERMONSTER = BIT(8); // incredibly powerful monster
const int MON_FLAG_RANGED = BIT(9); // monster shoots projectiles
const int MON_FLAG_MELEE = BIT(10);
METHOD(Monster, mr_setup, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) logic to run every frame */
METHOD(Monster, mr_think, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
+ /** (SERVER) logic to run every frame after the monster has died */
+ METHOD(Monster, mr_deadthink, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) called when monster dies */
METHOD(Monster, mr_death, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) called when monster is damaged */
// generated file; do not modify
+#include <common/monsters/monster/golem.qc>
#include <common/monsters/monster/mage.qc>
-#include <common/monsters/monster/shambler.qc>
#include <common/monsters/monster/spider.qc>
#include <common/monsters/monster/wyvern.qc>
#include <common/monsters/monster/zombie.qc>
// generated file; do not modify
+#include <common/monsters/monster/golem.qh>
#include <common/monsters/monster/mage.qh>
-#include <common/monsters/monster/shambler.qh>
#include <common/monsters/monster/spider.qh>
#include <common/monsters/monster/wyvern.qh>
#include <common/monsters/monster/zombie.qh>
--- /dev/null
+#include "golem.qh"
+
+#ifdef SVQC
+float autocvar_g_monster_golem_health;
+float autocvar_g_monster_golem_damageforcescale = 0.1;
+float autocvar_g_monster_golem_attack_smash_damage;
+float autocvar_g_monster_golem_attack_smash_force = 100;
+float autocvar_g_monster_golem_attack_smash_range = 200;
+float autocvar_g_monster_golem_attack_claw_damage;
+float autocvar_g_monster_golem_attack_lightning_damage;
+float autocvar_g_monster_golem_attack_lightning_damage_zap = 15;
+float autocvar_g_monster_golem_attack_lightning_force;
+float autocvar_g_monster_golem_attack_lightning_radius;
+float autocvar_g_monster_golem_attack_lightning_radius_zap;
+float autocvar_g_monster_golem_attack_lightning_speed;
+float autocvar_g_monster_golem_attack_lightning_speed_up;
+float autocvar_g_monster_golem_speed_stop;
+float autocvar_g_monster_golem_speed_run;
+float autocvar_g_monster_golem_speed_walk;
+
+.float golem_lastattack; // delay attacks separately
+
+void M_Golem_Attack_Smash(entity this)
+{
+ makevectors(this.angles);
+ Send_Effect(EFFECT_EXPLOSION_MEDIUM, (this.origin + (v_forward * 150)) - ('0 0 1' * this.maxs.z), '0 0 0', 1);
+ sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+
+ vector loc = this.origin + v_forward * 50;
+
+ entity dmgent = spawn();
+ dmgent.owner = dmgent.realowner = this;
+ setorigin(dmgent, loc);
+ RadiusDamage (dmgent, this, (autocvar_g_monster_golem_attack_smash_damage) * MONSTER_SKILLMOD(this), (autocvar_g_monster_golem_attack_smash_damage * 0.5) * MONSTER_SKILLMOD(this),
+ autocvar_g_monster_golem_attack_smash_range, this, NULL, autocvar_g_monster_golem_attack_smash_force, DEATH_MONSTER_GOLEM_SMASH.m_id, DMG_NOWEP, NULL);
+ delete(dmgent);
+}
+
+void M_Golem_Attack_Swing(entity this)
+{
+ Monster_Attack_Melee(this, this.enemy, (autocvar_g_monster_golem_attack_claw_damage), ((random() >= 0.5) ? this.anim_melee2 : this.anim_melee3), this.attack_range, 0.8, DEATH_MONSTER_GOLEM_CLAW.m_id, true);
+}
+
+#include <common/effects/qc/_mod.qh>
+
+void M_Golem_Attack_Lightning_Explode(entity this, entity directhitentity)
+{
+ sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
+
+ this.event_damage = func_null;
+ this.takedamage = DAMAGE_NO;
+ set_movetype(this, MOVETYPE_NONE);
+ this.velocity = '0 0 0';
+
+ if(this.move_movetype == MOVETYPE_NONE)
+ this.velocity = this.oldvelocity;
+
+ RadiusDamage (this, this.realowner, (autocvar_g_monster_golem_attack_lightning_damage), (autocvar_g_monster_golem_attack_lightning_damage), (autocvar_g_monster_golem_attack_lightning_radius),
+ NULL, NULL, (autocvar_g_monster_golem_attack_lightning_force), this.projectiledeathtype, DMG_NOWEP, directhitentity);
+
+ FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_golem_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
+ {
+ te_csqc_lightningarc(this.origin, it.origin);
+ Damage(it, this, this.realowner, (autocvar_g_monster_golem_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_GOLEM_ZAP.m_id, DMG_NOWEP, it.origin, '0 0 0');
+ });
+
+ setthink(this, SUB_Remove);
+ this.nextthink = time + 0.2;
+}
+
+void M_Golem_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger)
+{
+ M_Golem_Attack_Lightning_Explode(this, trigger);
+}
+
+void M_Golem_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+ if (GetResource(this, RES_HEALTH) <= 0)
+ return;
+
+ if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ TakeResource(this, RES_HEALTH, damage);
+
+ if (GetResource(this, RES_HEALTH) <= 0)
+ W_PrepareExplosionByDamage(this, attacker, adaptor_think2use);
+}
+
+void M_Golem_Attack_Lightning_Touch(entity this, entity toucher)
+{
+ PROJECTILE_TOUCH(this, toucher);
+
+ this.use(this, NULL, toucher);
+}
+
+void M_Golem_Attack_Lightning_Think(entity this)
+{
+ this.nextthink = time;
+ if (time > this.cnt)
+ {
+ M_Golem_Attack_Lightning_Explode(this, NULL);
+ return;
+ }
+}
+
+void M_Golem_Attack_Lightning(entity this)
+{
+ entity gren;
+
+ monster_makevectors(this, this.enemy);
+
+ gren = new(grenade);
+ gren.owner = gren.realowner = this;
+ gren.bot_dodge = true;
+ gren.bot_dodgerating = (autocvar_g_monster_golem_attack_lightning_damage);
+ set_movetype(gren, MOVETYPE_BOUNCE);
+ PROJECTILE_MAKETRIGGER(gren);
+ gren.projectiledeathtype = DEATH_MONSTER_GOLEM_ZAP.m_id;
+ setorigin(gren, CENTER_OR_VIEWOFS(this));
+ setsize(gren, '-8 -8 -8', '8 8 8');
+ gren.scale = 2.5;
+
+ gren.cnt = time + 5;
+ gren.nextthink = time;
+ setthink(gren, M_Golem_Attack_Lightning_Think);
+ gren.use = M_Golem_Attack_Lightning_Explode_use;
+ settouch(gren, M_Golem_Attack_Lightning_Touch);
+
+ gren.takedamage = DAMAGE_YES;
+ SetResourceExplicit(gren, RES_HEALTH, 50);
+ gren.damageforcescale = 0;
+ gren.event_damage = M_Golem_Attack_Lightning_Damage;
+ gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
+ gren.missile_flags = MIF_SPLASH | MIF_ARC;
+ W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_golem_attack_lightning_speed), (autocvar_g_monster_golem_attack_lightning_speed_up), 0, 0, false);
+
+ gren.angles = vectoangles (gren.velocity);
+ gren.flags = FL_PROJECTILE;
+ IL_PUSH(g_projectiles, gren);
+ IL_PUSH(g_bot_dodge, gren);
+
+ CSQCProjectile(gren, true, PROJECTILE_GOLEM_LIGHTNING, true);
+}
+
+.int state;
+
+bool M_Golem_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
+{
+ switch(attack_type)
+ {
+ case MONSTER_ATTACK_MELEE:
+ {
+ setanim(actor, ((random() >= 0.5) ? actor.anim_melee2 : actor.anim_melee3), false, true, true);
+ int swing_cnt = bound(1, floor(random() * 4), 3);
+ Monster_Delay(actor, swing_cnt, 0.5, M_Golem_Attack_Swing);
+ actor.anim_finished = actor.attack_finished_single[0] = time + (0.5 * swing_cnt); // set this for the delay
+ return true;
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ float randomness = random();
+
+ if(time < actor.golem_lastattack || !IS_ONGROUND(actor))
+ return false;
+
+ if(randomness <= 0.5 && vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_golem_attack_smash_range))
+ {
+ setanim(actor, actor.anim_melee1, false, true, true);
+ Monster_Delay(actor, 1, 1.1, M_Golem_Attack_Smash);
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1.2;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
+ actor.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
+ actor.golem_lastattack = time + 3 + random() * 1.5;
+ return true;
+ }
+ else if(randomness <= 0.1 && vdist(actor.enemy.origin - actor.origin, >=, autocvar_g_monster_golem_attack_smash_range * 1.5)) // small chance, don't want this spammed
+ {
+ setanim(actor, actor.anim_melee2, true, true, false);
+ actor.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
+ actor.attack_finished_single[0] = time + 1.1;
+ actor.anim_finished = 1.1;
+ actor.golem_lastattack = time + 3 + random() * 1.5;
+ Monster_Delay(actor, 1, 0.6, M_Golem_Attack_Lightning);
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+spawnfunc(monster_golem) { Monster_Spawn(this, true, MON_GOLEM); }
+// compatibility
+spawnfunc(monster_shambler) { spawnfunc_monster_golem(this); }
+#endif // SVQC
+
+#ifdef SVQC
+METHOD(Golem, mr_think, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ return true;
+}
+
+METHOD(Golem, mr_pain, float(Golem this, entity actor, float damage_take, entity attacker, float deathtype))
+{
+ TC(Golem, this);
+ actor.pain_finished = time + 0.5;
+ setanim(actor, ((random() >= 0.5) ? actor.anim_pain2 : actor.anim_pain1), true, true, false);
+ return damage_take;
+}
+
+METHOD(Golem, mr_death, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ setanim(actor, actor.anim_die1, false, true, true);
+ return true;
+}
+#endif
+#ifdef GAMEQC
+METHOD(Golem, mr_anim, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ vector none = '0 0 0';
+ actor.anim_idle = animfixfps(actor, '0 1 1', none);
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '2 1 1', none);
+ //actor.anim_melee1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
+ actor.anim_melee2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
+ actor.anim_melee3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ //actor.anim_melee4 = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_melee1 = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_pain1 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
+ actor.anim_pain2 = animfixfps(actor, '8 1 2', none); // 0.5 seconds
+ //actor.anim_pain3 = animfixfps(actor, '9 1 2', none); // 0.5 seconds
+ //actor.anim_pain4 = animfixfps(actor, '10 1 2', none); // 0.5 seconds
+ //actor.anim_pain5 = animfixfps(actor, '11 1 2', none); // 0.5 seconds
+ actor.anim_spawn = animfixfps(actor, '12 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '13 1 0.5', none); // 2 seconds
+ //actor.anim_dead = animfixfps(actor, '14 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '15 1 0.5', none); // 2 seconds
+ // dead2 16
+ //actor.anim_dieback = animfixfps(actor, '16 1 0.5', none); // 2 seconds
+ //actor.anim_deadback = animfixfps(actor, '17 1 0.5', none); // 2 seconds
+ //actor.anim_dead2 = animfixfps(actor, '18 1 0.5', none); // 2 seconds
+ //actor.anim_dead3 = animfixfps(actor, '19 1 0.5', none); // 2 seconds
+ //actor.anim_dead4 = animfixfps(actor, '20 1 0.5', none); // 2 seconds
+ //actor.anim_dead5 = animfixfps(actor, '21 1 0.5', none); // 2 seconds
+ //actor.anim_dead6 = animfixfps(actor, '22 1 0.5', none); // 2 seconds
+ return true;
+}
+#endif
+#ifdef SVQC
+.float animstate_endtime;
+METHOD(Golem, mr_setup, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ if(!GetResource(actor, RES_HEALTH)) SetResourceExplicit(actor, RES_HEALTH, autocvar_g_monster_golem_health);
+ if(!actor.attack_range) actor.attack_range = 150;
+ if(!actor.speed) { actor.speed = (autocvar_g_monster_golem_speed_walk); }
+ if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_golem_speed_run); }
+ if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_golem_speed_stop); }
+ if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_golem_damageforcescale); }
+
+ actor.monster_loot = ITEM_HealthMega;
+ actor.weapon = WEP_ELECTRO.m_id; // matches attacks better than WEP_VORTEX
+
+ setanim(actor, actor.anim_spawn, false, true, true);
+ actor.spawn_time = actor.animstate_endtime;
+ StatusEffects_apply(STATUSEFFECT_SpawnShield, actor, actor.spawn_time, 0);
+ actor.monster_attackfunc = M_Golem_Attack;
+
+ return true;
+}
+#endif
--- /dev/null
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_GOLEM, M_Model("golem.dpm"));
+#endif
+
+CLASS(Golem, Monster)
+ ATTRIB(Golem, spawnflags, int, MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
+ ATTRIB(Golem, m_mins, vector, '-24 -24 -20');
+ ATTRIB(Golem, m_maxs, vector, '24 24 88');
+#ifdef GAMEQC
+ ATTRIB(Golem, m_model, Model, MDL_MON_GOLEM);
+#endif
+ ATTRIB(Golem, netname, string, "golem");
+ ATTRIB(Golem, monster_name, string, _("Golem"));
+ENDCLASS(Golem)
+
+REGISTER_MONSTER(GOLEM, NEW(Golem));
#include "mage.qh"
#ifdef SVQC
+float autocvar_g_monster_mage_health;
+float autocvar_g_monster_mage_damageforcescale = 0.5;
+float autocvar_g_monster_mage_attack_spike_damage;
+float autocvar_g_monster_mage_attack_spike_radius;
+float autocvar_g_monster_mage_attack_spike_delay;
+float autocvar_g_monster_mage_attack_spike_accel;
+float autocvar_g_monster_mage_attack_spike_decel;
+float autocvar_g_monster_mage_attack_spike_chance = 0.45;
+float autocvar_g_monster_mage_attack_spike_turnrate;
+float autocvar_g_monster_mage_attack_spike_speed_max;
+float autocvar_g_monster_mage_attack_spike_smart;
+float autocvar_g_monster_mage_attack_spike_smart_trace_min;
+float autocvar_g_monster_mage_attack_spike_smart_trace_max;
+float autocvar_g_monster_mage_attack_spike_smart_mindist;
+float autocvar_g_monster_mage_attack_push_chance = 0.7;
+float autocvar_g_monster_mage_attack_push_damage;
+float autocvar_g_monster_mage_attack_push_radius;
+float autocvar_g_monster_mage_attack_push_delay;
+float autocvar_g_monster_mage_attack_push_force;
+float autocvar_g_monster_mage_attack_teleport_chance = 0.2;
+float autocvar_g_monster_mage_attack_teleport_delay = 2;
+float autocvar_g_monster_mage_attack_teleport_random = 0.4;
+float autocvar_g_monster_mage_attack_teleport_random_range = 1200;
+float autocvar_g_monster_mage_heal_self;
+float autocvar_g_monster_mage_heal_allies;
+float autocvar_g_monster_mage_heal_minhealth;
+float autocvar_g_monster_mage_heal_range;
+float autocvar_g_monster_mage_heal_delay;
+float autocvar_g_monster_mage_shield_time;
+float autocvar_g_monster_mage_shield_delay;
+float autocvar_g_monster_mage_shield_blockpercent;
+float autocvar_g_monster_mage_speed_stop;
+float autocvar_g_monster_mage_speed_run;
+float autocvar_g_monster_mage_speed_walk;
SOUND(MageSpike_FIRE, W_Sound("electro_fire"));
void M_Mage_Attack_Spike(entity this, vector dir);
OffhandMageTeleport OFFHAND_MAGE_TELEPORT;
STATIC_INIT(OFFHAND_MAGE_TELEPORT) { OFFHAND_MAGE_TELEPORT = NEW(OffhandMageTeleport); }
-float autocvar_g_monster_mage_health;
-float autocvar_g_monster_mage_damageforcescale = 0.5;
-float autocvar_g_monster_mage_attack_spike_damage;
-float autocvar_g_monster_mage_attack_spike_radius;
-float autocvar_g_monster_mage_attack_spike_delay;
-float autocvar_g_monster_mage_attack_spike_accel;
-float autocvar_g_monster_mage_attack_spike_decel;
-float autocvar_g_monster_mage_attack_spike_turnrate;
-float autocvar_g_monster_mage_attack_spike_speed_max;
-float autocvar_g_monster_mage_attack_spike_smart;
-float autocvar_g_monster_mage_attack_spike_smart_trace_min;
-float autocvar_g_monster_mage_attack_spike_smart_trace_max;
-float autocvar_g_monster_mage_attack_spike_smart_mindist;
-float autocvar_g_monster_mage_attack_push_damage;
-float autocvar_g_monster_mage_attack_push_radius;
-float autocvar_g_monster_mage_attack_push_delay;
-float autocvar_g_monster_mage_attack_push_force;
-float autocvar_g_monster_mage_heal_self;
-float autocvar_g_monster_mage_heal_allies;
-float autocvar_g_monster_mage_heal_minhealth;
-float autocvar_g_monster_mage_heal_range;
-float autocvar_g_monster_mage_heal_delay;
-float autocvar_g_monster_mage_shield_time;
-float autocvar_g_monster_mage_shield_delay;
-float autocvar_g_monster_mage_shield_blockpercent;
-float autocvar_g_monster_mage_speed_stop;
-float autocvar_g_monster_mage_speed_run;
-float autocvar_g_monster_mage_speed_walk;
-
-/*
-const float mage_anim_idle = 0;
-const float mage_anim_walk = 1;
-const float mage_anim_attack = 2;
-const float mage_anim_pain = 3;
-const float mage_anim_death = 4;
-const float mage_anim_run = 5;
-*/
-
void M_Mage_Defend_Heal(entity this);
void M_Mage_Defend_Shield(entity this);
// copied from W_Seeker_Think
void M_Mage_Attack_Spike_Think(entity this)
{
- if (time > this.ltime || (this.enemy && GetResource(this.enemy, RES_HEALTH) <= 0) || GetResource(this.owner, RES_HEALTH) <= 0) {
+ if (time > this.ltime || (this.enemy && GetResource(this.enemy, RES_HEALTH) <= 0) || !this.owner || GetResource(this.owner, RES_HEALTH) <= 0) {
this.projectiledeathtype |= HITTYPE_SPLASH;
M_Mage_Attack_Spike_Explode(this, NULL);
}
void M_Mage_Defend_Heal(entity this)
{
- float washealed = false;
+ bool washealed = false;
FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_mage_heal_range, M_Mage_Defend_Heal_Check(this, it),
{
}
case 1:
{
- if(GetResource(this, RES_CELLS)) GiveResourceWithLimit(it, RES_CELLS, 1, g_pickup_cells_max);
- if(GetResource(this, RES_PLASMA)) GiveResourceWithLimit(it, RES_PLASMA, 1, g_pickup_plasma_max);
- if(GetResource(this, RES_ROCKETS)) GiveResourceWithLimit(it, RES_ROCKETS, 1, g_pickup_rockets_max);
- if(GetResource(this, RES_SHELLS)) GiveResourceWithLimit(it, RES_SHELLS, 2, g_pickup_shells_max);
- if(GetResource(this, RES_BULLETS)) GiveResourceWithLimit(it, RES_BULLETS, 5, g_pickup_nails_max);
+ if(GetResource(it, RES_CELLS)) GiveResourceWithLimit(it, RES_CELLS, 1, g_pickup_cells_max);
+ if(GetResource(it, RES_PLASMA)) GiveResourceWithLimit(it, RES_PLASMA, 1, g_pickup_plasma_max);
+ if(GetResource(it, RES_ROCKETS)) GiveResourceWithLimit(it, RES_ROCKETS, 1, g_pickup_rockets_max);
+ if(GetResource(it, RES_SHELLS)) GiveResourceWithLimit(it, RES_SHELLS, 2, g_pickup_shells_max);
+ if(GetResource(it, RES_BULLETS)) GiveResourceWithLimit(it, RES_BULLETS, 5, g_pickup_nails_max);
// TODO: fuel?
fx = EFFECT_AMMO_REGEN;
break;
if(washealed)
{
- setanim(this, this.anim_shoot, true, true, true);
+ setanim(this, this.anim_melee, true, true, true);
this.attack_finished_single[0] = time + (autocvar_g_monster_mage_heal_delay);
+ this.state = MONSTER_ATTACK_MELEE;
this.anim_finished = time + 1.5;
}
}
NULL, NULL, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE.m_id, DMG_NOWEP, this.enemy);
Send_Effect(EFFECT_TE_EXPLOSION, this.origin, '0 0 0', 1);
- setanim(this, this.anim_shoot, true, true, true);
+ setanim(this, this.anim_duckjump, true, true, true);
this.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_push_delay);
+ this.anim_finished = time + 1;
+ this.state = MONSTER_ATTACK_MELEE; // prevent moving while firing spike
}
void M_Mage_Attack_Teleport(entity this, entity targ)
{
if(!targ) return;
- if(vdist(targ.origin - this.origin, >, 1500)) return;
+ if(vdist(targ.origin - this.origin, >, autocvar_g_monster_mage_attack_teleport_random_range)) return;
+
+ if(autocvar_g_monster_mage_attack_teleport_random && random() <= autocvar_g_monster_mage_attack_teleport_random)
+ {
+ vector oldpos = this.origin;
+ vector extrasize = '1 1 1' * autocvar_g_monster_mage_attack_teleport_random_range;
+ if(MoveToRandomLocationWithinBounds(this, this.absmin - extrasize, this.absmax + extrasize,
+ DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER,
+ Q3SURFACEFLAG_SKY, 10, 64, 256, true))
+ {
+ vector a = vectoangles(targ.origin - this.origin);
+ this.angles = '0 1 0' * a.y;
+ this.fixangle = true;
+ Send_Effect(EFFECT_SPAWN_NEUTRAL, oldpos, '0 0 0', 1);
+ Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+ this.attack_finished_single[0] = time + autocvar_g_monster_mage_attack_teleport_delay;
+ return;
+ }
+ }
+
+ if(!IS_ONGROUND(targ)) return;
makevectors(targ.angles);
- tracebox(targ.origin + ((v_forward * -1) * 200), this.mins, this.maxs, this.origin, MOVE_NOMONSTERS, this);
+ tracebox(CENTER_OR_VIEWOFS(targ), this.mins, this.maxs, CENTER_OR_VIEWOFS(targ) + ((v_forward * -1) * 200), MOVE_NOMONSTERS, this);
if(trace_fraction < 1)
return;
- vector newpos = targ.origin + ((v_forward * -1) * 200);
+ vector newpos = trace_endpos;
Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
Send_Effect(EFFECT_SPAWN_NEUTRAL, newpos, '0 0 0', 1);
this.fixangle = true;
this.velocity *= 0.5;
- this.attack_finished_single[0] = time + 0.2;
+ this.attack_finished_single[0] = time + autocvar_g_monster_mage_attack_teleport_delay;
}
void M_Mage_Defend_Shield(entity this)
this.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay);
SetResourceExplicit(this, RES_ARMOR, autocvar_g_monster_mage_shield_blockpercent);
setanim(this, this.anim_shoot, true, true, true);
- this.attack_finished_single[0] = time + 1;
+ this.attack_finished_single[0] = time + 1; // give just a short cooldown on attacking
this.anim_finished = time + 1;
}
{
case MONSTER_ATTACK_MELEE:
{
- if(random() <= 0.7)
+ if(random() <= autocvar_g_monster_mage_attack_push_chance)
{
Weapon wep = WEP_MAGE_SPIKE;
}
case MONSTER_ATTACK_RANGED:
{
- if(!actor.mage_spike)
+ if(random() <= autocvar_g_monster_mage_attack_teleport_chance)
{
- if(random() <= 0.4)
- {
- OffhandWeapon off = OFFHAND_MAGE_TELEPORT;
- off.offhand_think(off, actor, true);
- return true;
- }
- else
- {
- setanim(actor, actor.anim_shoot, true, true, true);
- actor.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay);
- actor.anim_finished = time + 1;
- Weapon wep = WEP_MAGE_SPIKE;
- wep.wr_think(wep, actor, weaponentity, 1);
- return true;
- }
+ OffhandWeapon off = OFFHAND_MAGE_TELEPORT;
+ actor.OffhandMageTeleport_key_pressed = 0;
+ off.offhand_think(off, actor, 1);
+ return true;
}
-
- if(actor.mage_spike)
+ else if(!actor.mage_spike && random() <= autocvar_g_monster_mage_attack_spike_chance)
+ {
+ setanim(actor, actor.anim_shoot, true, true, true);
+ actor.attack_finished_single[0] = time + (autocvar_g_monster_mage_attack_spike_delay);
+ actor.anim_finished = time + 1;
+ actor.state = MONSTER_ATTACK_MELEE; // prevent moving while firing spike
+ Weapon wep = WEP_MAGE_SPIKE;
+ wep.wr_think(wep, actor, weaponentity, 1);
return true;
- else
- return false;
+ }
+
+ return false;
}
}
METHOD(Mage, mr_death, bool(Mage this, entity actor))
{
TC(Mage, this);
- setanim(actor, actor.anim_die1, false, true, true);
+ setanim(actor, ((random() > 0.5) ? actor.anim_die2 : actor.anim_die1), false, true, true);
return true;
}
{
TC(Mage, this);
vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '4 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_pain1 = animfixfps(actor, '3 1 2', none); // 0.5 seconds
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '1 1 1', none);
actor.anim_shoot = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '5 1 1', none);
+ actor.anim_duckjump = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
+ actor.anim_melee = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ //actor.anim_fire1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
+ //actor.anim_fire2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
+ //actor.anim_fire3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ actor.anim_pain1 = animfixfps(actor, '6 1 2', none); // 0.5 seconds
+ actor.anim_pain2 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
+ //actor.anim_pain3 = animfixfps(actor, '8 1 2', none); // 0.5 seconds
+ actor.anim_die1 = animfixfps(actor, '9 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '10 1 0.5', none); // 2 seconds
+ //actor.anim_dead1 = animfixfps(actor, '11 1 0.5', none); // 2 seconds
+ //actor.anim_dead2 = animfixfps(actor, '12 1 0.5', none); // 2 seconds
return true;
}
#endif
#include "../all.qh"
#ifdef GAMEQC
-MODEL(MON_MAGE, M_Model("mage.dpm"));
+MODEL(MON_MAGE, M_Model("nanomage.dpm"));
#endif
CLASS(Mage, Monster)
ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
- ATTRIB(Mage, m_mins, vector, '-36 -36 -24');
- ATTRIB(Mage, m_maxs, vector, '36 36 50');
+ ATTRIB(Mage, m_mins, vector, '-16 -16 -24');
+ ATTRIB(Mage, m_maxs, vector, '16 16 55');
#ifdef GAMEQC
ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
#endif
+++ /dev/null
-#include "shambler.qh"
-
-#ifdef SVQC
-float autocvar_g_monster_shambler_health;
-float autocvar_g_monster_shambler_damageforcescale = 0.1;
-float autocvar_g_monster_shambler_attack_smash_damage;
-float autocvar_g_monster_shambler_attack_smash_range;
-float autocvar_g_monster_shambler_attack_claw_damage;
-float autocvar_g_monster_shambler_attack_lightning_damage;
-float autocvar_g_monster_shambler_attack_lightning_damage_zap = 15;
-float autocvar_g_monster_shambler_attack_lightning_force;
-float autocvar_g_monster_shambler_attack_lightning_radius;
-float autocvar_g_monster_shambler_attack_lightning_radius_zap;
-float autocvar_g_monster_shambler_attack_lightning_speed;
-float autocvar_g_monster_shambler_attack_lightning_speed_up;
-float autocvar_g_monster_shambler_speed_stop;
-float autocvar_g_monster_shambler_speed_run;
-float autocvar_g_monster_shambler_speed_walk;
-
-/*
-const float shambler_anim_stand = 0;
-const float shambler_anim_walk = 1;
-const float shambler_anim_run = 2;
-const float shambler_anim_smash = 3;
-const float shambler_anim_swingr = 4;
-const float shambler_anim_swingl = 5;
-const float shambler_anim_magic = 6;
-const float shambler_anim_pain = 7;
-const float shambler_anim_death = 8;
-*/
-
-.float shambler_lastattack; // delay attacks separately
-
-void M_Shambler_Attack_Smash(entity this)
-{
- makevectors(this.angles);
- Send_Effect(EFFECT_EXPLOSION_MEDIUM, (this.origin + (v_forward * 150)) - ('0 0 1' * this.maxs.z), '0 0 0', 1);
- sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
-
- // RadiusDamage does NOT support custom starting location, which means we must use this hack...
-
- tracebox(this.origin + v_forward * 50, this.mins * 0.5, this.maxs * 0.5, this.origin + v_forward * autocvar_g_monster_shambler_attack_smash_range, MOVE_NORMAL, this);
-
- if(trace_ent.takedamage)
- Damage(trace_ent, this, this, (autocvar_g_monster_shambler_attack_smash_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_SMASH.m_id, DMG_NOWEP, trace_ent.origin, normalize(trace_ent.origin - this.origin));
-}
-
-void M_Shambler_Attack_Swing(entity this)
-{
- Monster_Attack_Melee(this, this.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((random() >= 0.5) ? this.anim_melee2 : this.anim_melee3), this.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW.m_id, true);
-}
-
-#include <common/effects/qc/_mod.qh>
-
-void M_Shambler_Attack_Lightning_Explode(entity this, entity directhitentity)
-{
- sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
- Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
-
- this.event_damage = func_null;
- this.takedamage = DAMAGE_NO;
- set_movetype(this, MOVETYPE_NONE);
- this.velocity = '0 0 0';
-
- if(this.move_movetype == MOVETYPE_NONE)
- this.velocity = this.oldvelocity;
-
- RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius),
- NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, DMG_NOWEP, directhitentity);
-
- FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_shambler_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
- {
- te_csqc_lightningarc(this.origin, it.origin);
- Damage(it, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, DMG_NOWEP, it.origin, '0 0 0');
- });
-
- setthink(this, SUB_Remove);
- this.nextthink = time + 0.2;
-}
-
-void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger)
-{
- M_Shambler_Attack_Lightning_Explode(this, trigger);
-}
-
-void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
-{
- if (GetResource(this, RES_HEALTH) <= 0)
- return;
-
- if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
- return; // g_projectiles_damage says to halt
-
- TakeResource(this, RES_HEALTH, damage);
-
- if (GetResource(this, RES_HEALTH) <= 0)
- W_PrepareExplosionByDamage(this, attacker, adaptor_think2use);
-}
-
-void M_Shambler_Attack_Lightning_Touch(entity this, entity toucher)
-{
- PROJECTILE_TOUCH(this, toucher);
-
- this.use(this, NULL, toucher);
-}
-
-void M_Shambler_Attack_Lightning_Think(entity this)
-{
- this.nextthink = time;
- if (time > this.cnt)
- {
- M_Shambler_Attack_Lightning_Explode(this, NULL);
- return;
- }
-}
-
-void M_Shambler_Attack_Lightning(entity this)
-{
- entity gren = new(grenade);
- gren.owner = gren.realowner = this;
- gren.bot_dodge = true;
- gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage);
- set_movetype(gren, MOVETYPE_BOUNCE);
- PROJECTILE_MAKETRIGGER(gren);
- gren.projectiledeathtype = DEATH_MONSTER_SHAMBLER_ZAP.m_id;
- setorigin(gren, CENTER_OR_VIEWOFS(this));
- setsize(gren, '-8 -8 -8', '8 8 8');
- gren.scale = 2.5;
-
- gren.cnt = time + 5;
- gren.nextthink = time;
- setthink(gren, M_Shambler_Attack_Lightning_Think);
- gren.use = M_Shambler_Attack_Lightning_Explode_use;
- settouch(gren, M_Shambler_Attack_Lightning_Touch);
-
- gren.takedamage = DAMAGE_YES;
- SetResourceExplicit(gren, RES_HEALTH, 50);
- gren.damageforcescale = 0;
- gren.event_damage = M_Shambler_Attack_Lightning_Damage;
- gren.damagedbycontents = true;
- IL_PUSH(g_damagedbycontents, gren);
- gren.missile_flags = MIF_SPLASH | MIF_ARC;
- W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
-
- gren.angles = vectoangles (gren.velocity);
- gren.flags = FL_PROJECTILE;
- IL_PUSH(g_projectiles, gren);
- IL_PUSH(g_bot_dodge, gren);
-
- CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
-}
-
-.int state;
-
-bool M_Shambler_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
-{
- switch(attack_type)
- {
- case MONSTER_ATTACK_MELEE:
- {
- int swing_cnt = bound(1, floor(random() * 4), 3);
- Monster_Delay(actor, swing_cnt, 0.5, M_Shambler_Attack_Swing);
- actor.anim_finished = actor.attack_finished_single[0] = time + (0.5 * swing_cnt); // set this for the delay
- return true;
- }
- case MONSTER_ATTACK_RANGED:
- {
- float randomness = random();
-
- if(time >= actor.shambler_lastattack) // shambler doesn't attack much
- if(IS_ONGROUND(actor))
- if(randomness <= 0.5 && vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_shambler_attack_smash_range))
- {
- setanim(actor, actor.anim_melee2, true, true, false);
- Monster_Delay(actor, 1, 0.7, M_Shambler_Attack_Smash);
- actor.attack_finished_single[0] = time + 1.1;
- actor.anim_finished = time + 1.1;
- actor.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
- actor.shambler_lastattack = time + 3 + random() * 1.5;
- return true;
- }
- else if(randomness <= 0.1 && vdist(actor.enemy.origin - actor.origin, >=, autocvar_g_monster_shambler_attack_smash_range * 1.5)) // small chance, don't want this spammed
- {
- setanim(actor, actor.anim_shoot, true, true, false);
- actor.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
- actor.attack_finished_single[0] = time + 1.1;
- actor.anim_finished = 1.1;
- actor.shambler_lastattack = time + 3 + random() * 1.5;
- Monster_Delay(actor, 1, 0.6, M_Shambler_Attack_Lightning);
- return true;
- }
-
- return false;
- }
- }
-
- return false;
-}
-
-spawnfunc(monster_shambler) { Monster_Spawn(this, true, MON_SHAMBLER); }
-#endif // SVQC
-
-#ifdef SVQC
-METHOD(Shambler, mr_think, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- return true;
-}
-
-METHOD(Shambler, mr_pain, float(Shambler this, entity actor, float damage_take, entity attacker, float deathtype))
-{
- TC(Shambler, this);
- actor.pain_finished = time + 0.5;
- setanim(actor, actor.anim_pain1, true, true, false);
- return damage_take;
-}
-
-METHOD(Shambler, mr_death, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- setanim(actor, actor.anim_die1, false, true, true);
- return true;
-}
-#endif
-#ifdef GAMEQC
-METHOD(Shambler, mr_anim, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '8 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
- actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_pain1 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
- actor.anim_melee1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
- actor.anim_melee2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
- actor.anim_melee3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
- actor.anim_shoot = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '2 1 1', none);
- return true;
-}
-#endif
-#ifdef SVQC
-.float animstate_endtime;
-METHOD(Shambler, mr_setup, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- if(!GetResource(this, RES_HEALTH)) SetResourceExplicit(actor, RES_HEALTH, autocvar_g_monster_shambler_health);
- if(!actor.attack_range) actor.attack_range = 150;
- if(!actor.speed) { actor.speed = (autocvar_g_monster_shambler_speed_walk); }
- if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_shambler_speed_run); }
- if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_shambler_speed_stop); }
- if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_shambler_damageforcescale); }
-
- actor.monster_loot = ITEM_HealthMega;
- actor.weapon = WEP_ELECTRO.m_id; // matches attacks better than WEP_VORTEX
-
- setanim(actor, actor.anim_shoot, false, true, true);
- actor.spawn_time = actor.animstate_endtime;
- StatusEffects_apply(STATUSEFFECT_SpawnShield, actor, actor.spawn_time, 0);
- actor.monster_attackfunc = M_Shambler_Attack;
-
- return true;
-}
-#endif
+++ /dev/null
-#pragma once
-
-#include "../all.qh"
-
-#ifdef GAMEQC
-MODEL(MON_SHAMBLER, M_Model("shambler.mdl"));
-#endif
-
-CLASS(Shambler, Monster)
- ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
- ATTRIB(Shambler, m_mins, vector, '-41 -41 -31');
- ATTRIB(Shambler, m_maxs, vector, '41 41 65');
-#ifdef GAMEQC
- ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
-#endif
- ATTRIB(Shambler, netname, string, "shambler");
- ATTRIB(Shambler, monster_name, string, _("Golem"));
-ENDCLASS(Shambler)
-
-REGISTER_MONSTER(SHAMBLER, NEW(Shambler));
#endif
#ifdef SVQC
-
-.float spider_web_delay;
-
+float autocvar_g_monster_spider_health;
+float autocvar_g_monster_spider_damageforcescale = 0.6;
+float autocvar_g_monster_spider_attack_bite_damage;
+float autocvar_g_monster_spider_attack_bite_delay;
float autocvar_g_monster_spider_attack_web_damagetime;
float autocvar_g_monster_spider_attack_web_speed;
float autocvar_g_monster_spider_attack_web_speed_up;
float autocvar_g_monster_spider_attack_web_delay;
+float autocvar_g_monster_spider_attack_web_range = 800;
+float autocvar_g_monster_spider_speed_stop;
+float autocvar_g_monster_spider_speed_run;
+float autocvar_g_monster_spider_speed_walk;
-float autocvar_g_monster_spider_attack_bite_damage;
-float autocvar_g_monster_spider_attack_bite_delay;
+.float spider_web_delay;
void M_Spider_Attack_Web(entity this);
TC(SpiderAttack, thiswep);
bool isPlayer = IS_PLAYER(actor);
if (fire & 1)
- if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay)) {
+ if ((!isPlayer && time >= actor.spider_web_delay) || (isPlayer && weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay))) {
if (!isPlayer) {
- actor.spider_web_delay = time + 3;
+ actor.spider_web_delay = time + autocvar_g_monster_spider_attack_web_delay;
setanim(actor, actor.anim_shoot, true, true, true);
- actor.attack_finished_single[0] = time + (autocvar_g_monster_spider_attack_web_delay);
- actor.anim_finished = time + 1;
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
}
if (isPlayer) actor.enemy = Monster_FindTarget(actor);
monster_makevectors(actor, actor.enemy);
}
}
-float autocvar_g_monster_spider_health;
-float autocvar_g_monster_spider_damageforcescale = 0.6;
-float autocvar_g_monster_spider_speed_stop;
-float autocvar_g_monster_spider_speed_run;
-float autocvar_g_monster_spider_speed_walk;
-
-/*
-const float spider_anim_idle = 0;
-const float spider_anim_walk = 1;
-const float spider_anim_attack = 2;
-const float spider_anim_attack2 = 3;
-*/
-
void M_Spider_Attack_Web_Explode(entity this)
{
if(this)
}
case MONSTER_ATTACK_RANGED:
{
- wep.wr_think(wep, actor, weaponentity, 1);
- return true;
+ if(vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_spider_attack_web_range))
+ {
+ wep.wr_think(wep, actor, weaponentity, 1);
+ return true;
+ }
}
}
METHOD(Spider, mr_pain, float(Spider this, entity actor, float damage_take, entity attacker, float deathtype))
{
TC(Spider, this);
+ setanim(actor, ((random() > 0.5) ? actor.anim_pain2 : actor.anim_pain1), true, true, false);
+ actor.pain_finished = actor.animstate_endtime;
return damage_take;
}
METHOD(Spider, mr_death, bool(Spider this, entity actor))
{
TC(Spider, this);
- setanim(actor, actor.anim_melee, false, true, true);
- actor.angles_x = 180;
+ setanim(actor, ((random() > 0.5) ? actor.anim_die2 : actor.anim_die1), false, true, true);
return true;
}
#endif
{
TC(Spider, this);
vector none = '0 0 0';
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
- actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_melee = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_shoot = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '1 1 1', none);
+ actor.anim_melee = animfixfps(actor, '0 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '1 1 1', none);
+ actor.anim_die2 = animfixfps(actor, '2 1 1', none);
+ actor.anim_shoot = animfixfps(actor, '3 1 1', none);
+ //actor.anim_fire2 = animfixfps(actor, '4 1 1', none);
+ actor.anim_idle = animfixfps(actor, '5 1 1', none);
+ //actor.anim_sight = animfixfps(actor, '6 1 1', none);
+ actor.anim_pain1 = animfixfps(actor, '7 1 1', none);
+ actor.anim_pain2 = animfixfps(actor, '8 1 1', none);
+ //actor.anim_pain3 = animfixfps(actor, '9 1 1', none);
+ actor.anim_walk = animfixfps(actor, '10 1 1', none);
+ actor.anim_run = animfixfps(actor, '10 1 1', none); // temp?
+ //actor.anim_forwardright = animfixfps(actor, '11 1 1', none);
+ //actor.anim_walkright = animfixfps(actor, '12 1 1', none);
+ //actor.anim_walkbackright = animfixfps(actor, '13 1 1', none);
+ //actor.anim_walkback = animfixfps(actor, '14 1 1', none);
+ //actor.anim_walkbackleft = animfixfps(actor, '15 1 1', none);
+ //actor.anim_walkleft = animfixfps(actor, '16 1 1', none);
+ //actor.anim_forwardleft = animfixfps(actor, '17 1 1', none);
return true;
}
#endif
CLASS(Spider, Monster)
ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
- ATTRIB(Spider, m_mins, vector, '-18 -18 -25');
- ATTRIB(Spider, m_maxs, vector, '18 18 30');
+ ATTRIB(Spider, m_mins, vector, '-30 -30 -25');
+ ATTRIB(Spider, m_maxs, vector, '30 30 30');
#ifdef GAMEQC
ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
#endif
#include "wyvern.qh"
#ifdef SVQC
-
+float autocvar_g_monster_wyvern_health;
+float autocvar_g_monster_wyvern_damageforcescale = 0.6;
float autocvar_g_monster_wyvern_attack_fireball_damage;
float autocvar_g_monster_wyvern_attack_fireball_edgedamage;
float autocvar_g_monster_wyvern_attack_fireball_damagetime;
float autocvar_g_monster_wyvern_attack_fireball_force;
float autocvar_g_monster_wyvern_attack_fireball_radius;
float autocvar_g_monster_wyvern_attack_fireball_speed;
+float autocvar_g_monster_wyvern_speed_stop;
+float autocvar_g_monster_wyvern_speed_run;
+float autocvar_g_monster_wyvern_speed_walk;
void M_Wyvern_Attack_Fireball_Explode(entity this);
void M_Wyvern_Attack_Fireball_Touch(entity this, entity toucher);
return true;
}
-float autocvar_g_monster_wyvern_health;
-float autocvar_g_monster_wyvern_damageforcescale = 0.6;
-float autocvar_g_monster_wyvern_speed_stop;
-float autocvar_g_monster_wyvern_speed_run;
-float autocvar_g_monster_wyvern_speed_walk;
-
-/*
-const float wyvern_anim_hover = 0;
-const float wyvern_anim_fly = 1;
-const float wyvern_anim_magic = 2;
-const float wyvern_anim_pain = 3;
-const float wyvern_anim_death = 4;
-*/
-
void M_Wyvern_Attack_Fireball_Explode(entity this)
{
Send_Effect(EFFECT_FIREBALL_EXPLODE, this.origin, '0 0 0', 1);
entity own = this.realowner;
RadiusDamage(this, own, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force,
- NULL, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, DMG_NOWEP, NULL);
+ own, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, DMG_NOWEP, NULL);
- FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM,
+ FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM && it != own,
{
Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype);
});
M_Wyvern_Attack_Fireball_Explode(this);
}
+void M_Wyvern_Attack_Fireball(entity this)
+{
+ w_shotdir = normalize((this.enemy.origin + '0 0 10') - this.origin);
+ Weapon wep = WEP_WYVERN_ATTACK;
+ // TODO
+ .entity weaponentity = weaponentities[0];
+ wep.wr_think(wep, this, weaponentity, 1);
+}
+
bool M_Wyvern_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
{
switch(attack_type)
case MONSTER_ATTACK_MELEE:
case MONSTER_ATTACK_RANGED:
{
- w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
- Weapon wep = WEP_WYVERN_ATTACK;
- wep.wr_think(wep, actor, weaponentity, 1);
+ Monster_Delay(actor, 0, 1, M_Wyvern_Attack_Fireball);
+ //actor.anim_finished = time + 1.2;
+ setanim(actor, actor.anim_shoot, false, true, true);
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1.2;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
return true;
}
}
return true;
}
+METHOD(Wyvern, mr_deadthink, bool(Wyvern this, entity actor))
+{
+ TC(Wyvern, this);
+ if(IS_ONGROUND(actor))
+ setanim(actor, actor.anim_die2, true, false, false);
+ return true;
+}
+
METHOD(Wyvern, mr_pain, float(Wyvern this, entity actor, float damage_take, entity attacker, float deathtype))
{
TC(Wyvern, this);
{
TC(Wyvern, this);
vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '4 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
actor.anim_idle = animfixfps(actor, '0 1 1', none);
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '2 1 1', none);
actor.anim_pain1 = animfixfps(actor, '3 1 2', none); // 0.5 seconds
- actor.anim_shoot = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '1 1 1', none);
+ actor.anim_pain2 = animfixfps(actor, '4 1 2', none); // 0.5 seconds
+ actor.anim_melee = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ actor.anim_shoot = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '7 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '8 1 0.5', none); // 2 seconds
return true;
}
#endif
#include "../all.qh"
#ifdef GAMEQC
-MODEL(MON_WYVERN, M_Model("wizard.mdl"));
+MODEL(MON_WYVERN, M_Model("wyvern.dpm"));
#endif
CLASS(Wyvern, Monster)
- ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
- ATTRIB(Wyvern, m_mins, vector, '-20 -20 -58');
- ATTRIB(Wyvern, m_maxs, vector, '20 20 20');
+ ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MON_FLAG_RANGED | MON_FLAG_RIDE);
+ ATTRIB(Wyvern, m_mins, vector, '-30 -30 -48');
+ ATTRIB(Wyvern, m_maxs, vector, '30 30 30');
#ifdef GAMEQC
ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
#endif
float autocvar_g_monster_zombie_speed_run;
float autocvar_g_monster_zombie_speed_walk;
-/*
-const float zombie_anim_attackleap = 0;
-const float zombie_anim_attackrun1 = 1;
-const float zombie_anim_attackrun2 = 2;
-const float zombie_anim_attackrun3 = 3;
-const float zombie_anim_attackstanding1 = 4;
-const float zombie_anim_attackstanding2 = 5;
-const float zombie_anim_attackstanding3 = 6;
-const float zombie_anim_blockend = 7;
-const float zombie_anim_blockstart = 8;
-const float zombie_anim_deathback1 = 9;
-const float zombie_anim_deathback2 = 10;
-const float zombie_anim_deathback3 = 11;
-const float zombie_anim_deathfront1 = 12;
-const float zombie_anim_deathfront2 = 13;
-const float zombie_anim_deathfront3 = 14;
-const float zombie_anim_deathleft1 = 15;
-const float zombie_anim_deathleft2 = 16;
-const float zombie_anim_deathright1 = 17;
-const float zombie_anim_deathright2 = 18;
-const float zombie_anim_idle = 19;
-const float zombie_anim_painback1 = 20;
-const float zombie_anim_painback2 = 21;
-const float zombie_anim_painfront1 = 22;
-const float zombie_anim_painfront2 = 23;
-const float zombie_anim_runbackwards = 24;
-const float zombie_anim_runbackwardsleft = 25;
-const float zombie_anim_runbackwardsright = 26;
-const float zombie_anim_runforward = 27;
-const float zombie_anim_runforwardleft = 28;
-const float zombie_anim_runforwardright = 29;
-const float zombie_anim_spawn = 30;
-*/
-
.vector moveto;
void M_Zombie_Attack_Leap_Touch(entity this, entity toucher)
{
TC(Zombie, this);
actor.pain_finished = time + 0.34;
- setanim(actor, ((random() > 0.5) ? actor.anim_pain1 : actor.anim_pain2), true, true, false);
+ if(time >= actor.spawn_time)
+ setanim(actor, ((random() > 0.5) ? actor.anim_pain1 : actor.anim_pain2), true, true, false);
return damage_take;
}
}
}
+bool monster_facing(entity this, entity targ)
+{
+ // relies on target having an origin
+ makevectors(this.angles);
+ vector targ_org = targ.origin, my_org = this.origin;
+ if(autocvar_g_monsters_target_infront_2d)
+ {
+ targ_org = vec2(targ_org);
+ my_org = vec2(my_org);
+ }
+ float dot = normalize(targ_org - my_org) * v_forward;
+
+ return !(dot <= autocvar_g_monsters_target_infront_range);
+}
+
void monster_makevectors(entity this, entity targ)
{
if(IS_MONSTER(this))
// Target handling
// ===============
-bool Monster_ValidTarget(entity this, entity targ)
+bool Monster_ValidTarget(entity this, entity targ, bool skipfacing)
{
// ensure we're not checking nonexistent monster/target
if(!this || !targ) { return false; }
if((targ == this)
- || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
|| (IS_VEHICLE(targ) && !(this.monsterdef.spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
|| (time < game_starttime) // monsters do nothing before match has started
|| (targ.takedamage == DAMAGE_NO)
|| (SAME_TEAM(targ, this))
|| (STAT(FROZEN, targ))
|| (targ.alpha != 0 && targ.alpha < 0.5)
+ || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
|| (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
)
{
}
vector targ_origin = ((targ.absmin + targ.absmax) * 0.5);
- traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this);
+ traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this); // TODO: maybe we can rely a bit on PVS data instead?
if(trace_fraction < 1 && trace_ent != targ)
return false; // solid
- if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
+ if(!skipfacing && (autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)))
if(this.enemy != targ)
{
- makevectors (this.angles);
- float dot = normalize (targ.origin - this.origin) * v_forward;
-
- if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
+ if(!monster_facing(this, targ))
+ return false;
}
return true; // this target is valid!
vector my_center = CENTER_OR_VIEWOFS(this);
// find the closest acceptable target to pass to
- IL_EACH(g_monster_targets, it.monster_attack && vdist(it.origin - this.origin, <, this.target_range),
+ IL_EACH(g_monster_targets, it.monster_attack,
{
- if(Monster_ValidTarget(this, it))
- {
- // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in damage.qc)
- vector targ_center = CENTER_OR_VIEWOFS(it);
+ float trange = this.target_range;
+ if(PHYS_INPUT_BUTTON_CROUCH(it))
+ trange *= 0.75; // TODO cvar this
+ vector theirmid = (it.absmin + it.absmax) * 0.5;
+ if(vdist(theirmid - this.origin, >, trange))
+ continue;
+ if(!Monster_ValidTarget(this, it, false))
+ continue;
- if(closest_target)
- {
- vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
- if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
- { closest_target = it; }
- }
- else { closest_target = it; }
+ // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+ vector targ_center = CENTER_OR_VIEWOFS(it);
+
+ if(closest_target)
+ {
+ vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+ if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
+ { closest_target = it; }
}
+ else { closest_target = it; }
});
return closest_target;
else
{
if(this.monster_skill <= MONSTER_SKILL_EASY)
- this.colormap = 1029;
+ this.colormap = 1126;
else if(this.monster_skill <= MONSTER_SKILL_MEDIUM)
- this.colormap = 1027;
+ this.colormap = 1075;
else if(this.monster_skill <= MONSTER_SKILL_HARD)
- this.colormap = 1038;
+ this.colormap = 1228;
else if(this.monster_skill <= MONSTER_SKILL_INSANE)
- this.colormap = 1028;
+ this.colormap = 1092;
else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE)
- this.colormap = 1032;
+ this.colormap = 1160;
else
this.colormap = 1024;
}
+
+ if(this.colormap > 0)
+ this.glowmod = colormapPaletteColor(this.colormap & 0x0F, false);
+ else
+ this.glowmod = '1 1 1';
}
void monster_changeteam(entity this, int newteam)
.void(entity) monster_delayedfunc;
void Monster_Delay_Action(entity this)
{
- if(Monster_ValidTarget(this.owner, this.owner.enemy))
+ // TODO: maybe do check for facing here
+ if(Monster_ValidTarget(this.owner, this.owner.enemy, false))
{
monster_makevectors(this.owner, this.owner.enemy);
this.monster_delayedfunc(this.owner);
string sample = this.(samplefield);
if (sample != "") sample = GlobalSound_sample(sample, random());
float myscale = ((this.scale) ? this.scale : 1); // safety net
- // TODO: change volume depending on size too?
sound7(this, chan, sample, VOL_BASE, ATTEN_NORM, 100 / myscale, 0);
this.msound_delay = time + sound_delay;
if((!this || !targ)
|| (!this.monster_attackfunc)
+ || (game_stopped)
|| (time < this.attack_finished_single[slot])
+ || ((autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) && !monster_facing(this, targ))
) { return; }
if(vdist(targ.origin - this.origin, <=, this.attack_range))
void Monster_Touch(entity this, entity toucher)
{
- if(toucher == NULL) { return; }
+ if(!toucher) { return; }
- if(toucher.monster_attack)
- if(this.enemy != toucher)
- if(!IS_MONSTER(toucher))
- if(Monster_ValidTarget(this, toucher))
+ if(toucher.monster_attack && this.enemy != toucher && !IS_MONSTER(toucher) && time >= this.spawn_time)
+ if(Monster_ValidTarget(this, toucher, true))
this.enemy = toucher;
}
void Monster_Use(entity this, entity actor, entity trigger)
{
- if(Monster_ValidTarget(this, actor)) { this.enemy = actor; }
+ if(Monster_ValidTarget(this, actor, true)) { this.enemy = actor; }
}
-.float pass_distance;
vector Monster_Move_Target(entity this, entity targ)
{
// enemy is always preferred target
{
vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5);
targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us)
- WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
-
- // cases where the enemy may have changed their state (don't need to check everything here)
- if((!this.enemy)
- || (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1)
- || (STAT(FROZEN, this.enemy))
- || (this.enemy.flags & FL_NOTARGET)
- || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
- || (this.enemy.takedamage == DAMAGE_NO)
- || (vdist(this.origin - targ_origin, >, this.target_range))
- || ((trace_fraction < 1) && (trace_ent != this.enemy)))
- {
- this.enemy = NULL;
- //this.pass_distance = 0;
- }
if(this.enemy)
{
}
}
-void Monster_CalculateVelocity(entity this, vector to, vector from, float turnrate, float movespeed)
-{
- //float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
- //float initial_height = 0; //min(50, (targ_distance * tanh(20)));
- //float current_height = (initial_height * min(1, (this.pass_distance) ? (current_distance / this.pass_distance) : current_distance));
- //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
-
- vector targpos = to;
-#if 0
- if(current_height) // make sure we can actually do this arcing path
- {
- targpos = (to + ('0 0 1' * current_height));
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1)
- {
- //print("normal arc line failed, trying to find new pos...");
- WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, this);
- targpos = (trace_endpos + '0 0 -10');
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ }
- /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */
- }
- }
- else { targpos = to; }
-#endif
-
- //this.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
-
- vector desired_direction = normalize(targpos - from);
- if(turnrate) { this.velocity = (normalize(normalize(this.velocity) + (desired_direction * 50)) * movespeed); }
- else { this.velocity = (desired_direction * movespeed); }
-
- //this.steerto = steerlib_attract2(targpos, 0.5, 500, 0.95);
- //this.angles = vectoangles(this.velocity);
-}
-
.entity draggedby;
void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
if(!(this.spawnflags & MONSTERFLAG_FLY_VERTICAL) && !(this.flags & FL_SWIM))
this.moveto_z = this.origin_z;
- if(vdist(this.origin - this.moveto, >, 100))
+ fixedmakevectors(this.angles);
+ float vz = this.velocity_z;
+
+ if(!turret_closetotarget(this, this.moveto, 16))
{
bool do_run = (this.enemy || this.monster_moveto);
- if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
- Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed));
+ movelib_move_simple(this, v_forward, ((do_run) ? runspeed : walkspeed), 0.4);
- if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!?
+ if(time > this.pain_finished && time > this.anim_finished)
if(!this.state)
{
if(vdist(this.velocity, >, 10))
setanim(this, this.anim_idle, true, false, false);
}
+ if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
+ this.velocity_z = vz;
+
this.steerto = steerlib_attract2(this, ((this.monster_face) ? this.monster_face : this.moveto), 0.5, 500, 0.95);
vector real_angle = vectoangles(this.steerto) - this.angles;
{
this.nextthink = time + this.ticrate;
+ Monster mon = REGISTRY_GET(Monsters, this.monsterid);
+ mon.mr_deadthink(mon, this);
+
if(this.monster_lifetime != 0)
if(time >= this.monster_lifetime)
{
this.state = 0;
this.attack_finished_single[0] = 0;
this.effects = 0;
+ this.dphitcontentsmask &= ~DPCONTENTS_BODY;
if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
this.velocity = '0 0 0';
void Monster_Enemy_Check(entity this)
{
- if(!this.enemy)
+ if(this.enemy)
{
- this.enemy = Monster_FindTarget(this);
- if(this.enemy)
+ vector targ_origin = ((this.enemy.absmin + this.enemy.absmax) * 0.5);
+ targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us)
+ WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
+
+ // cases where the enemy may have changed their state (don't need to check everything here)
+ if( (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1)
+ || (STAT(FROZEN, this.enemy))
+ || (this.enemy.flags & FL_NOTARGET)
+ || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
+ || (this.enemy.takedamage == DAMAGE_NO)
+ || (vdist(this.origin - targ_origin, >, this.target_range))
+ || ((trace_fraction < 1) && (trace_ent != this.enemy))
+ )
{
- WarpZone_RefSys_Copy(this.enemy, this);
- WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver
- // update move target immediately?
- this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax)));
- this.monster_moveto = '0 0 0';
- this.monster_face = '0 0 0';
-
- //this.pass_distance = vlen((('1 0 0' * this.enemy.origin_x) + ('0 1 0' * this.enemy.origin_y)) - (('1 0 0' * this.origin_x) + ('0 1 0' * this.origin_y)));
- Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE);
+ this.enemy = NULL;
}
+ else
+ {
+ return;
+ }
+ }
+
+ this.enemy = Monster_FindTarget(this);
+ if(this.enemy)
+ {
+ WarpZone_RefSys_Copy(this.enemy, this);
+ WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver
+ // update move target immediately?
+ this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax)));
+ this.monster_moveto = '0 0 0';
+ this.monster_face = '0 0 0';
+
+ Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE);
}
}
this.max_health = GetResource(this, RES_HEALTH);
this.pain_finished = this.nextthink;
+ this.last_enemycheck = this.spawn_time + random(); // slight delay
if(IS_PLAYER(this.monster_follow))
this.effects |= EF_DIMLIGHT;
else
setmodel(this, mon.m_model);
+ if(!this.monster_name || this.monster_name == "")
+ this.monster_name = mon.monster_name;
+
if(this.statuseffects && this.statuseffects.owner == this)
{
StatusEffects_clearall(this.statuseffects);
this.reset = Monster_Reset;
this.netname = mon.netname;
this.monster_attackfunc = mon.monster_attackfunc;
- this.monster_name = mon.monster_name;
this.candrop = true;
- this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.oldtarget2 = this.target2;
- //this.pass_distance = 0;
this.deadflag = DEAD_NO;
this.spawn_time = time;
this.gravity = 1;
if(autocvar_g_nodepthtestplayers) { this.effects |= EF_NODEPTHTEST; }
if(mon.spawnflags & MONSTER_TYPE_SWIM) { this.flags |= FL_SWIM; }
- if(autocvar_g_playerclip_collisions)
+ if(autocvar_g_monsters_playerclip_collisions)
this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
if(mon.spawnflags & MONSTER_TYPE_FLY)
set_movetype(this, MOVETYPE_FLY);
}
- if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
- {
- if(mon.spawnflags & MONSTER_SIZE_BROKEN)
- this.scale *= 1.3;
-
- if(mon.spawnflags & MONSTER_SIZE_QUAKE)
- if(autocvar_g_monsters_quake_resize)
- this.scale *= 1.3;
- }
+ if((mon.spawnflags & MONSTER_SIZE_QUAKE) && autocvar_g_monsters_quake_resize && !(this.spawnflags & MONSTERFLAG_RESPAWNED))
+ this.scale *= 1.3;
setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale);
+ this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);
float autocvar_g_monsters_target_range;
bool autocvar_g_monsters_target_infront;
float autocvar_g_monsters_target_infront_range = 0.3;
+bool autocvar_g_monsters_target_infront_2d = true;
float autocvar_g_monsters_attack_range;
int autocvar_g_monsters_score_kill;
int autocvar_g_monsters_score_spawned;
bool autocvar_g_monsters_typefrag;
bool autocvar_g_monsters_owners;
+bool autocvar_g_monsters_playerclip_collisions;
float autocvar_g_monsters_miniboss_chance;
float autocvar_g_monsters_miniboss_healthboost;
float autocvar_g_monsters_drop_time;
void nade_monster_boom(entity this)
{
+ if(!autocvar_g_monsters)
+ return;
entity e = spawn();
e.noalign = true; // don't drop to floor
e = spawnmonster(e, this.pokenade_type, MON_Null, this.realowner, this.realowner, this.origin, false, false, 1);
+ if(!e)
+ return; // monster failed to be spawned
if(autocvar_g_nades_pokenade_monster_lifetime > 0)
e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
nade_blast = false;
break;
case NADE_TYPE_MONSTER:
+ if(!autocvar_g_monsters)
+ {
+ expef = EFFECT_NADE_EXPLODE(this.realowner.team);
+ break; // fall back to a standard nade explosion
+ }
case NADE_TYPE_SPAWN:
nade_blast = false;
switch(this.realowner.team)
MSG_INFO_NOTIF(DEATH_SELF_GENERIC, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 died%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_LAVA, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_lava", _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s"))
MSG_INFO_NOTIF(DEATH_SELF_MON_MAGE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was exploded by a Mage%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Golem%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Golem%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was zapped to death by a Golem%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_CLAW, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Golem%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_SMASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Golem%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_ZAP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was zapped to death by a Golem%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_SPIDER, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was bitten by a Spider%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_WYVERN, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 joins the Zombies%s%s"), "")
MSG_MULTI_NOTIF(DEATH_SELF_GENERIC, N_ENABLE, NULL, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_GENERIC)
MSG_MULTI_NOTIF(DEATH_SELF_LAVA, N_ENABLE, NULL, INFO_DEATH_SELF_LAVA, CENTER_DEATH_SELF_LAVA)
MSG_MULTI_NOTIF(DEATH_SELF_MON_MAGE, N_ENABLE, NULL, INFO_DEATH_SELF_MON_MAGE, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_CLAW, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_SMASH, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_ZAP, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_CLAW, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_CLAW, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_SMASH, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_SMASH, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_ZAP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_ZAP, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_SPIDER, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SPIDER, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_WYVERN, N_ENABLE, NULL, INFO_DEATH_SELF_MON_WYVERN, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, CENTER_DEATH_SELF_MONSTER)
.vector glowmod;
void turret_changeteam(entity this)
{
- this.glowmod = Team_ColorRGB(this.team - 1) * 2;
+ this.glowmod = Team_ColorRGB(this.team - 1);
this.teamradar_color = Team_ColorRGB(this.team - 1);
if(this.team)
#undef TRY
}
-bool turret_closetotarget(entity this, vector targ)
+bool turret_closetotarget(entity this, vector targ, float range)
{
- vector path_extra_size = '64 64 64';
+ vector path_extra_size = '1 1 1' * range;
return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size);
}
bool turret_initialize(entity this, Turret tur);
// returns true when box overlaps with a given location
-bool turret_closetotarget(entity this, vector targ);
+bool turret_closetotarget(entity this, vector targ, float range);
/// Function to use for target evaluation. usualy turret_targetscore_generic
.float(entity _turret, entity _target) turret_score_target;
void ewheel_move_path(entity this)
{
// Are we close enough to a path node to switch to the next?
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
{
#ifdef EWHEEL_FANCYPATH
if (this.pathcurrent.path_next == NULL)
{
#ifdef WALKER_FANCYPATHING
// Are we close enougth to a path node to switch to the next?
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
{
if (this.pathcurrent.path_next == NULL)
{
walker_move_to(this, this.moveto, 0);
#else
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
this.pathcurrent = this.pathcurrent.enemy;
if(!this.pathcurrent)
#include "all.inc"
-// TODO: remove after 0.8.2. Retains impulse number compatibility because 0.8.1 clients don't reload the weapons.cfg
-#define WEP_HARDCODED_IMPULSES 20
-
// TODO: invert after 0.8.2. Will require moving 'best weapon' impulses
#define WEP_IMPULSE_BEGIN 230
#define WEP_IMPULSE_END bound(WEP_IMPULSE_BEGIN, WEP_IMPULSE_BEGIN + (REGISTRY_COUNT(Weapons) - 1) - 1, 253)
-REGISTRY_SORT(Weapons, WEP_HARDCODED_IMPULSES + 1)
+REGISTRY_SORT(Weapons, 1)
REGISTRY_CHECK(Weapons)
STATIC_INIT(register_weapons_done)
vector weaponentity_glowmod(Weapon wep, entity actor, int c, entity wepent)
{
vector g;
- if (!(g = wep.wr_glow(wep, actor, wepent))) g = colormapPaletteColor(c & 0x0F, true) * 2;
+ if (!(g = wep.wr_glow(wep, actor, wepent))) g = colormapPaletteColor(c & 0x0F, true);
return g;
}
const int PROJECTILE_BUMBLE_BEAM = 31;
const int PROJECTILE_MAGE_SPIKE = 32;
-const int PROJECTILE_SHAMBLER_LIGHTNING = 33;
+const int PROJECTILE_GOLEM_LIGHTNING = 33;
const int PROJECTILE_ROCKETMINSTA_LASER = 34;
if(IS_DUCKED(actor))
spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod);
- W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage), thiswep.m_id | HITTYPE_SECONDARY);
+ W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage) * WEP_CVAR_SEC(hlac, shots), thiswep.m_id | HITTYPE_SECONDARY);
W_MuzzleFlash(thiswep, actor, weaponentity, w_shotorg, w_shotdir);
+ W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hlac, ammo), weaponentity);
- missile = new(hlacbolt);
- missile.owner = missile.realowner = actor;
- missile.bot_dodge = true;
+ for(int j = WEP_CVAR_SEC(hlac, shots); j > 0; --j)
+ {
+ missile = new(hlacbolt);
+ missile.owner = missile.realowner = actor;
+ missile.bot_dodge = true;
- missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage);
+ missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage);
- set_movetype(missile, MOVETYPE_FLY);
- PROJECTILE_MAKETRIGGER(missile);
+ set_movetype(missile, MOVETYPE_FLY);
+ PROJECTILE_MAKETRIGGER(missile);
- setorigin(missile, w_shotorg);
- setsize(missile, '0 0 0', '0 0 0');
+ setorigin(missile, w_shotorg);
+ setsize(missile, '0 0 0', '0 0 0');
- W_SetupProjVelocity_Basic(missile, WEP_CVAR_SEC(hlac, speed), spread);
- //missile.angles = vectoangles(missile.velocity); // csqc
+ W_SetupProjVelocity_Basic(missile, WEP_CVAR_SEC(hlac, speed), spread);
+ //missile.angles = vectoangles(missile.velocity); // csqc
- settouch(missile, W_HLAC_Touch);
- setthink(missile, SUB_Remove);
+ settouch(missile, W_HLAC_Touch);
+ setthink(missile, SUB_Remove);
- missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime);
+ missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime);
- missile.flags = FL_PROJECTILE;
- IL_PUSH(g_projectiles, missile);
- IL_PUSH(g_bot_dodge, missile);
- missile.missile_flags = MIF_SPLASH;
- missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
- missile.weaponentity_fld = weaponentity;
+ missile.flags = FL_PROJECTILE;
+ IL_PUSH(g_projectiles, missile);
+ IL_PUSH(g_bot_dodge, missile);
+ missile.missile_flags = MIF_SPLASH;
+ missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY;
+ missile.weaponentity_fld = weaponentity;
- CSQCProjectile(missile, true, PROJECTILE_HLAC, true);
+ CSQCProjectile(missile, true, PROJECTILE_HLAC, true);
- MUTATOR_CALLHOOK(EditProjectile, actor, missile);
+ MUTATOR_CALLHOOK(EditProjectile, actor, missile);
+ }
+
+ if(!autocvar_g_norecoil)
+ {
+ actor.punchangle_x = random() - 0.5;
+ actor.punchangle_y = random() - 0.5;
+ }
}
// weapon frames
}
}
-void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor, .entity weaponentity)
-{
- W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hlac, ammo), weaponentity);
-
- for(float i = WEP_CVAR_SEC(hlac, shots); i > 0; --i)
- W_HLAC_Attack2(thiswep, actor, weaponentity);
-
- if(!autocvar_g_norecoil)
- {
- actor.punchangle_x = random() - 0.5;
- actor.punchangle_y = random() - 0.5;
- }
-}
-
METHOD(HLAC, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
{
PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), false);
{
if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hlac, refire)))
{
- W_HLAC_Attack2_Frame(thiswep, actor, weaponentity);
+ W_HLAC_Attack2(thiswep, actor, weaponentity);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
}
}
me.TR(me);
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "zombie", _("Zombie")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "spider", _("Spider")));
- me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "shambler", _("Golem")));
+ me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "golem", _("Golem")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "mage", _("Mage")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "wyvern", _("Wyvern")));
me.TR(me);
}
}
-void DisableServerBackwardsCompatibility()
-{
- cvar_set("gameversion_min", ftos(100 * floor(cvar("gameversion") / 100)));
-}
-
void UpdateNotification_URI_Get_Callback(float id, float status, string data)
{
float n;
}
}
- if(un_version != "")
+ if(un_version != "" && vercmp(cvar_string("g_xonoticversion"), un_version) < 0)
{
- if(vercmp(cvar_string("g_xonoticversion"), un_version) < 0)
- {
- // update needed
- _Nex_ExtResponseSystem_UpdateTo = strzone(un_version);
- if(un_download) { LOG_INFO(_("Update can be downloaded at:"), "\n", un_download); }
- if(un_url) { _Nex_ExtResponseSystem_UpdateToURL = strzone(un_url); }
- DisableServerBackwardsCompatibility();
- }
- else if(cvar_string("g_xonoticversion") == un_version)
- {
- if(un_compatexpire != "")
- {
- string curdate = strftime(false, "%Y%m%d%H%M%S");
- if (strcmp(curdate, un_compatexpire) >= 0)
- DisableServerBackwardsCompatibility();
- }
- }
+ // update needed
+ _Nex_ExtResponseSystem_UpdateTo = strzone(un_version);
+ if(un_download) { LOG_INFO(_("Update can be downloaded at:"), "\n", un_download); }
+ if(un_url) { _Nex_ExtResponseSystem_UpdateToURL = strzone(un_url); }
}
if(un_tosversion != "")
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
/* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
- /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
/**/
// hidden gametypes come last so indexing always works correctly
#define HIDDEN_GAMETYPES \
GAMETYPE(MAPINFO_TYPE_RACE) \
GAMETYPE(MAPINFO_TYPE_CTS) \
+ GAMETYPE(MAPINFO_TYPE_INVASION) \
/**/
Gametype GameType_GetID(int cnt)
else if(time < CS(caller).jointime + MIN_SPEC_TIME)
CS(caller).autojoin_checked = -1;
}
+ CS(caller).parm_idlesince = time;
return; // never fall through to usage
}
string mon_oldname = mon.monster_name;
mon.monster_name = argument;
- if (mon.sprite) WaypointSprite_UpdateSprites(mon.sprite, WP_Monster, WP_Null, WP_Null);
+ if (mon.sprite) WaypointSprite_UpdateSprites(mon.sprite, WP_Monster, WP_Null, WP_Null); // TODO: the new name is never actually sent to CSQC!
print_to(caller, sprintf("Your pet '%s' is now known as '%s'", mon_oldname, mon.monster_name));
return;
}
{
KillPlayerForTeamChange(player);
PlayerScore_Clear(player); // works only in game modes without teams
- CS(player).parm_idlesince = time;
if (!IS_BOT_CLIENT(player))
TeamBalance_AutoBalanceBots();
BADCVAR("g_duel_not_dm_maps");
BADCVAR("g_freezetag");
BADCVAR("g_freezetag_teams");
- BADCVAR("g_invasion_teams");
BADCVAR("g_invasion_type");
BADCVAR("g_jailbreak");
BADCVAR("g_jailbreak_teams");
// does nothing gameplay relevant
BADCVAR("captureleadlimit_override");
BADCVAR("condump_stripcolors");
- BADCVAR("gameversion");
BADCVAR("fs_gamedir");
BADCVAR("g_allow_oldvortexbeam");
BADCVAR("g_balance_kill_delay");
BADCVAR("w_prop_interval");
BADPREFIX("chat_");
BADPREFIX("crypto_");
- BADPREFIX("gameversion_");
+ BADPREFIX("gameversion");
BADPREFIX("g_chat_");
BADPREFIX("g_ctf_captimerecord_");
BADPREFIX("g_hats_");
BADPREFIX("sv_timeout_");
BADPREFIX("sv_vote_");
BADPREFIX("timelimit_");
+ BADPRESUFFIX("g_", "_round_timelimit");
// allowed changes to server admins (please sync this to server.cfg)
// vi commands:
BADCVAR("g_grappling_hook");
BADCVAR("g_jetpack");
- // temporary for testing
- // TODO remove before 0.8.3 release
- BADCVAR("g_ca_weaponarena");
- BADCVAR("g_freezetag_weaponarena");
- BADCVAR("g_lms_weaponarena");
- BADCVAR("g_ctf_stalemate_time");
-
#undef BADPRESUFFIX
#undef BADPREFIX
#undef BADCVAR
delete(this);
}
-bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance)
+bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
{
float m = e.dphitcontentsmask;
e.dphitcontentsmask = goodcontents | badcontents;
continue;
// rule 4: we must "see" some spawnpoint or item
- entity sp = NULL;
- IL_EACH(g_spawnpoints, checkpvs(mstart, it),
- {
- if((traceline(mstart, it.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
- {
- sp = it;
- break;
- }
- });
+ entity sp = NULL;
+ if(frompos)
+ {
+ if((traceline(mstart, e.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
+ sp = e;
+ }
+ if(!sp)
+ {
+ IL_EACH(g_spawnpoints, checkpvs(mstart, it),
+ {
+ if((traceline(mstart, it.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
+ {
+ sp = it;
+ break;
+ }
+ });
+ }
if(!sp)
{
int items_checked = 0;
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
{
- return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance);
+ return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance, false);
}
/*
void DumpStats(float final);
-bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance);
+bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos);
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance);
--- /dev/null
+golem
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/golem.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file
--- /dev/null
+nanomage
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/nanomage.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file
}
}
+megaerebus
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/megaerebus.tga
+ rgbgen lightingDiffuse
+ }
+}
+
shadowhead
{
dpreflectcube cubemaps/default/sky
rgbgen lightingDiffuse
}
}
+
+shadowheadfb
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/shadowheadfb.tga
+ rgbgen lightingDiffuse
+ }
+}
rgbgen lightingDiffuse
}
}
+
ignisfullbright
{
dpreflectcube cubemaps/default/sky
rgbgen lightingDiffuse
}
}
+
+ignisheadfb
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/ignisheadfb.tga
+ rgbgen lightingDiffuse
+ }
+}
--- /dev/null
+spider
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/spider.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file
+++ /dev/null
-// TODO: remove after 0.8.2. Default impulse commands for 0.8.1 servers
-alias weapon_blaster "impulse 230"
-alias weapon_shotgun "impulse 231"
-alias weapon_machinegun "impulse 232"
-alias weapon_mortar "impulse 233"
-alias weapon_minelayer "impulse 234"
-alias weapon_electro "impulse 235"
-alias weapon_crylink "impulse 236"
-alias weapon_vortex "impulse 237"
-alias weapon_hagar "impulse 238"
-alias weapon_devastator "impulse 239"
-alias weapon_porto "impulse 240"
-alias weapon_vaporizer "impulse 241"
-alias weapon_hook "impulse 242"
-alias weapon_hlac "impulse 243"
-alias weapon_tuba "impulse 244"
-alias weapon_rifle "impulse 245"
-alias weapon_fireball "impulse 246"
-alias weapon_seeker "impulse 247"
-alias weapon_shockwave "impulse 248"
-alias weapon_arc "impulse 249"
+++ /dev/null
-#!/bin/sh
-<qcsrc/server/w_all.qc perl -ne 'BEGIN{$i=230}/w_(.*?)\./ or+next;printf qq{alias weapon_%s "impulse %d"\n},$1,$i++;'
alias weapon_group_8 "impulse 8"
alias weapon_group_9 "impulse 9"
alias weapon_group_0 "impulse 14" // cycles the superweapons
-// TODO: remove after 0.8.2. Default impulse commands for 0.8.1 servers
-exec weapons.cfg
cl_curl_enabled 1
cl_curl_maxdownloads 3
seta cl_loddistance1 1024
seta cl_loddistance2 3072
seta cl_playerdetailreduction 4 "the higher, the less detailed player models are displayed (LOD)"
-seta cl_modeldetailreduction 1 "the higher, the less detailed certain map models are displayed (LOD)"
+seta cl_modeldetailreduction 1 "higher values will reduce the detail of non-player and non-weapon models (LOD)"
seta cl_casings_maxcount 100 "maximum amount of shell casings (must be at least 1)"
seta cl_gibs_maxcount 100 "maximum amount of gibs (must be at least 1)"
// Xonotic version (formatted for machines)
// used to determine if a client version is compatible
-// this doesn't have to be bumped with every release
+// general policy: previous release has "best effort" support, older releases are unsupported,
+// but gameversion_min doesn't have to be bumped with every release
// bump when clients become incompatible or any other perfectly good reason
// (e.g. game data incompatibility, engine version incompatibility, etc
// note: this automatically filters the server browser, clients of the new
// e.g. Xonotic 1.5.1 RC1 will be 15101
set g_xonoticversion git "Xonotic version (formatted for humans)"
-gameversion 802 // 0.8.2
-gameversion_min 0 // git builds see all versions
-gameversion_max 65535 // git builds see all versions
+gameversion 805 // 0.8.5
+gameversion_min 802 // 0.8.2 is the previous release
+gameversion_max 65535 // future versions should nag players to update
// compatibility guideline:
// version a.b.c = a0b0c