├── .gitattributes ├── .gitignore ├── README.md ├── descript.ion ├── progs.dat ├── quake.rc ├── smp.fgd ├── smp_readme.txt └── src ├── ai.qc ├── amtest.qc ├── boss.qc ├── buttons.qc ├── changelog.txt ├── client.qc ├── combat.qc ├── defs.qc ├── demon.qc ├── dog.qc ├── doors.qc ├── enforcer.qc ├── fight.qc ├── fish.qc ├── flag.qc ├── hknight.qc ├── items.qc ├── jctest.qc ├── knight.qc ├── misc.qc ├── models.qc ├── monsters.qc ├── ogre.qc ├── oldone.qc ├── plats.qc ├── player.qc ├── progs.src ├── shalrath.qc ├── shambler.qc ├── soldier.qc ├── spawn.qc ├── sprites.qc ├── subs.qc ├── triggers.qc ├── weapons.qc ├── wizard.qc ├── world.qc └── zombie.qc /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | maps/ 2 | qconsole.log 3 | *.sav 4 | desktop.ini 5 | *.cfg 6 | *.lno 7 | *.spr 8 | *.dat 9 | *.png 10 | *.dat 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMP 2 | 3 | ### Speed Mapping progs (SMP) by dumptruck_ds 4 | 5 | This is a lightweight Quake mod and devkit. It retains the core gameplay of the 6 | original game while offering popular quality-of-life features for mappers. It is 7 | also an excellent base for new QuakeC projects, as it includes a comprehensive 8 | list of bug fixes; more than other "clean code" versions of QuakeC. 9 | 10 | INSTALLATION 11 | 12 | Unzip the "smp" folder into your Quake directory. Then launch Quake with the 13 | -game parameter on the command line. 14 | 15 | e.g C:\Quake -game smp 16 | 17 | If you want to make a map or episode with smp, simply rename "smp" as you desire 18 | and release it as you would any mod or map. Just make sure to include the files 19 | that came with smp. (don't forget to delete any configs or extra unwanted 20 | files!) 21 | 22 | FEATURES 23 | 24 | * trigger spawn monsters 25 | * items, weapons, ammo and artifacts can be trigger spawned, set to respawn and 26 | be suspended in mid air 27 | * is_waiting key for dormant trigger_once and trigger_multiple 28 | * set custom gravity via worldspawn 29 | * built-in "Rune hack" for start maps and hub levels (info_player_start2) 30 | * fish are gib-able 31 | * target thru target4 for more complex gameplay setups 32 | * a basic misc_model entity, useful for decorations 33 | * three custom sound entities 34 | * single player instagib 35 | 36 | FIXES 37 | 38 | There are many bug fixes from the Quake Info Pool, progs_dump and other mods. 39 | Take a look at changelog.txt in the 'src' folder for details. 40 | 41 | FGD 42 | 43 | The smp.fgd file is the same as the default included with TrenchBroom 2023.1 44 | with added spawnflags, keys and entities noted below. 45 | 46 | SPAWNFLAGS 47 | 48 | * trigger spawn: Set this to spawn monsters and items. A targetname is required. 49 | * spawn angry: Monsters near the player will spawn angry when trigger spawned. 50 | * spawn silently: Entities and monsters will spawn and respawn silently with no 51 | particles. 52 | * respawn particles: When respawning, entities will display particles and make 53 | a sound. 54 | * suspended in air: Place items in mid-air 55 | * info_player_start2: Set on trigger_changelevel to spawn the exiting player at 56 | * an info_player_start2. Useful for start or hub maps. This takes the place of 57 | Rune hacks. 58 | 59 | KEY / VALUES 60 | 61 | * multiple targets per entity (target, target2, target3 and target4) 62 | * killtarget and killtarget2 63 | * gravity: Set this in the worldspawn entity for custom gravity across the entire 64 | map. 100 is the gravity used in "Ziggurat Vertigo" (e1m8). 800 is the default 65 | gravity in Quake. There is no need to set this to the default, leave it blank. 66 | * is_waiting: When set to 1, on trigger_once and trigger_multiple, this trigger 67 | will do nothing until another trigger activates it. A targetname is required. 68 | * wait: Items, artifacts, ammo, health and weapons will respawn if 'wait' is 69 | greater than zero. Set 'wait' to how many seconds you want between spawns. 70 | 71 | ENTITIES 72 | 73 | Usage for these entities are detailed in the fgd: 74 | 75 | * misc_model: a simple entity to display a model 76 | * play_sound_triggered: trigger a custom sound on demand 77 | * play_sound_random: play a custom sound at random intervals 78 | * ambient_general: plays a custom loop-able sound 79 | 80 | SINGLE PLAYER INSTAGIB 81 | 82 | Works on any id1 compatible map. Set your desired mode in the console: 83 | 84 | * gib_all - instagib everyone 85 | * gib_monsters - only monsters instagib 86 | * gib_player - only players instagib 87 | * gib_off - disables instagib 88 | 89 | You can also enable them on the command line: 90 | 91 | * fraglimit 666 - instagib everyone 92 | * fraglimit 333 - only monsters instagib 93 | * fraglimit 999 - only players instagib 94 | * fraglimit 0 - disables instagib 95 | 96 | ### CONTACT 97 | 98 | If you have any questions contact dumptruck_ds at: 99 | lango.lan.party@gmail.com or dumptruck_ds#3116 via Discord 100 | 101 | Enjoy! 102 | -------------------------------------------------------------------------------- /descript.ion: -------------------------------------------------------------------------------- 1 | Speed Mapping Progs 2024.2 2 | -------------------------------------------------------------------------------- /progs.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quake-mapping-community/smp/3ab7459b2daeed4ea85dfa87b6d4dcf1c03a7d8a/progs.dat -------------------------------------------------------------------------------- /quake.rc: -------------------------------------------------------------------------------- 1 | 2 | // load the base configuration 3 | exec default.cfg 4 | 5 | // load the last saved configuration 6 | exec config.cfg 7 | 8 | // run a user script file if present 9 | exec autoexec.cfg 10 | 11 | // 12 | // stuff command line statements 13 | // 14 | stuffcmds 15 | 16 | // start demos if not already running a server 17 | startdemos demo1 demo2 demo3 18 | 19 | //sp-instagib commands 20 | alias gib_all "fraglimit 666" 21 | alias gib_monsters "fraglimit 333" 22 | alias gib_player "fraglimit 999" 23 | alias gib_off "fraglimit 0" 24 | -------------------------------------------------------------------------------- /smp_readme.txt: -------------------------------------------------------------------------------- 1 | Speed Mapping progs (SMP) 2 | Version 2024.2 3 | by dumptruck_ds 4 | 5 | This is a lightweight Quake mod and devkit. It retains the core gameplay of the 6 | original game while offering popular quality-of-life features for mappers. It is 7 | also an excellent base for new QuakeC projects, as it includes a comprehensive 8 | list of bug fixes; more than other "clean code" versions of QuakeC. The GitHub 9 | repository can be found here: https://github.com/quake-mapping-community/smp 10 | 11 | INSTALLATION 12 | 13 | Unzip the "smp" folder into your Quake directory. Then launch Quake with the 14 | -game parameter on the command line. 15 | 16 | e.g C:\Quake -game smp 17 | 18 | If you want to make a map or episode with smp, simply rename "smp" as you desire 19 | and release it as you would any mod or map. Just make sure to include the files 20 | that came with smp. (don't forget to delete any configs or extra unwanted 21 | files!) 22 | 23 | FEATURES 24 | 25 | * trigger spawn monsters 26 | * items, weapons, ammo and artifacts can be trigger spawned, set to respawn and 27 | be suspended in mid air 28 | * is_waiting key for dormant trigger_once and trigger_multiple 29 | * set custom gravity via worldspawn 30 | * built-in "Rune hack" for start maps and hub levels (info_player_start2) 31 | * fish are gib-able 32 | * target thru target4 for more complex gameplay setups 33 | * a basic misc_model entity, useful for decorations 34 | * three custom sound entities 35 | * single player instagib 36 | 37 | FIXES 38 | 39 | There are countless bug fixes from the Quake Info Pool, progs_dump and other 40 | mods. Too many to include here, but you can review the commits on GitHub: 41 | https://github.com/quake-mapping-community/smp/commits/main or take a look at 42 | changelog.txt in the 'src' folder. 43 | 44 | FGD 45 | 46 | The smp.fgd file is the same as the default included with TrenchBroom 2023.1 47 | with added spawnflags, keys and entities noted below. 48 | 49 | SPAWNFLAGS 50 | 51 | * trigger spawn: Set this to spawn monsters and items. A targetname is required. 52 | * spawn angry: Monsters near the player will spawn angry when trigger spawned. 53 | * spawn silently: Entities and monsters will spawn and respawn silently with no 54 | particles. 55 | * respawn particles: When respawning, entities will display particles and make 56 | a sound. 57 | * suspended in air: Place items in mid-air 58 | * info_player_start2: Set on trigger_changelevel to spawn the exiting player at 59 | * an info_player_start2. Useful for start or hub maps. This takes the place of 60 | Rune hacks. 61 | 62 | KEY / VALUES 63 | 64 | * multiple targets per entity (target, target2, target3 and target4) 65 | * killtarget and killtarget2 66 | * gravity: Set this in the worldspawn entity for custom gravity across the entire 67 | map. 100 is the gravity used in "Ziggurat Vertigo" (e1m8). 800 is the default 68 | gravity in Quake. There is no need to set this to the default, leave it blank. 69 | * is_waiting: When set to 1, on trigger_once and trigger_multiple, this trigger 70 | will do nothing until another trigger activates it. A targetname is required. 71 | * wait: Items, artifacts, ammo, health and weapons will respawn if 'wait' is 72 | greater than zero. Set 'wait' to how many seconds you want between spawns. 73 | 74 | ENTITIES 75 | 76 | Usage for these entities are detailed in the fgd: 77 | 78 | * misc_model: a simple entity to display a model 79 | * play_sound_triggered: trigger a custom sound on demand 80 | * play_sound_random: play a custom sound at random intervals 81 | * ambient_general: plays a custom loop-able sound 82 | 83 | SINGLE PLAYER / COOP INSTAGIB 84 | 85 | Works on any id1 compatible map. Set your desired mode in the console: 86 | 87 | * gib_all - instagib everyone 88 | * gib_monsters - only monsters instagib 89 | * gib_player - only players instagib 90 | * gib_off - disables instagib 91 | 92 | You can also enable them on the command line: 93 | 94 | * fraglimit 666 - instagib everyone 95 | * fraglimit 333 - only monsters instagib 96 | * fraglimit 999 - only players instagib 97 | * fraglimit 0 - disables instagib 98 | 99 | Use "teamplay 1" on the server to disable friendly fire in coop. 100 | 101 | KNOWN ISSUES 102 | 103 | If a door or other moving object collides with the spawn point of a trigger 104 | spawned entity, the entity could be pushed or "drop out" of the level. To avoid 105 | this, don't have doors or other moving platforms intersecting with an entity's 106 | spawn point. However, you can have items spawn atop moving trains and lifts. 107 | 108 | SOURCES USED FOR FIXES 109 | 110 | Quake Info Pools fixes: 111 | https://www.quake-info-pool.net/q1/qcfix.htm 112 | 113 | Khreathor's mod_jam_progs: 114 | http://khreathor.xyz/jam/modding/mod_jam_progs.zip 115 | 116 | Ultimate Ranger Quake Patch (URQP) 117 | http://www.quaketastic.com/files/misc/urqp106a.zip 118 | 119 | progs_dump: 120 | https://github.com/progs-dump-dev/progs_dump 121 | 122 | If you have any questions contact dumptruck_ds at: 123 | lango.lan.party@gmail.com or dumptruck_ds#3116 via Discord 124 | 125 | Enjoy! 126 | -------------------------------------------------------------------------------- /src/amtest.qc: -------------------------------------------------------------------------------- 1 | /*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~> 2 | ~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/ 3 | 4 | void() test_teleport_touch; 5 | void() tele_done; 6 | 7 | /*QUAKED test_teleport (0 .5 .8) ? 8 | Teleporter testing 9 | */ 10 | void() test_teleport = 11 | { 12 | precache_model ("sprites/s_aball.spr"); 13 | setsize (self, self.mins, self.maxs); 14 | self.touch = test_teleport_touch; 15 | self.solid = 1; 16 | 17 | if (!self.target) 18 | objerror ("no target\n"); 19 | }; 20 | 21 | void() test_teleport_touch = 22 | { 23 | local entity oldself; 24 | other.movetype = MOVETYPE_TOSS; 25 | // other.solid = SOLID_NOT; 26 | other.dest = '256 -128 -128'; 27 | oldself = self; 28 | self = other; 29 | // SUB_CalcMove (self.dest, 200, tele_done); 30 | self.velocity = '1000 0 0 '; 31 | self = oldself; 32 | }; 33 | 34 | void() tele_done = 35 | { 36 | self.movetype = MOVETYPE_WALK; 37 | self.solid = SOLID_SLIDEBOX; 38 | }; 39 | 40 | /*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~> 41 | ~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/ 42 | 43 | void() test_goaway; 44 | void() test_spawn; 45 | 46 | /*QUAKED test_fodder (0 .5 .8) ? 47 | beating guy 48 | */ 49 | void() test_fodder = 50 | { 51 | self.nextthink = time + 3; 52 | self.think = test_spawn; 53 | }; 54 | 55 | void() test_spawn = 56 | { 57 | local entity body; 58 | makevectors (self.angles); 59 | 60 | body = spawn(); 61 | setmodel (body, "progs/soldier.mdl"); 62 | setorigin (body, self.origin); 63 | body.classname = "player"; 64 | body.health = 1000; 65 | body.frags = 0; 66 | body.takedamage = DAMAGE_AIM; 67 | body.solid = SOLID_SLIDEBOX; 68 | body.movetype = MOVETYPE_WALK; 69 | body.show_hostile = 0; 70 | body.weapon = 1; 71 | body.velocity = v_forward * 200; 72 | 73 | body.nextthink = time + 5; 74 | body.think = test_goaway; 75 | 76 | self.nextthink = time + 3; 77 | self.think = test_spawn; 78 | 79 | }; 80 | 81 | void() test_goaway = 82 | { 83 | remove (self); 84 | }; 85 | 86 | -------------------------------------------------------------------------------- /src/boss.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | BOSS-ONE 5 | 6 | ============================================================================== 7 | */ 8 | $cd id1/models/boss1 9 | $origin 0 0 -15 10 | $base base 11 | $skin skin 12 | $scale 5 13 | 14 | $frame rise1 rise2 rise3 rise4 rise5 rise6 rise7 rise8 rise9 rise10 15 | $frame rise11 rise12 rise13 rise14 rise15 rise16 rise17 16 | 17 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 18 | $frame walk9 walk10 walk11 walk12 walk13 walk14 walk15 19 | $frame walk16 walk17 walk18 walk19 walk20 walk21 walk22 20 | $frame walk23 walk24 walk25 walk26 walk27 walk28 walk29 walk30 walk31 21 | 22 | $frame death1 death2 death3 death4 death5 death6 death7 death8 death9 23 | 24 | $frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8 25 | $frame attack9 attack10 attack11 attack12 attack13 attack14 attack15 26 | $frame attack16 attack17 attack18 attack19 attack20 attack21 attack22 27 | $frame attack23 28 | 29 | $frame shocka1 shocka2 shocka3 shocka4 shocka5 shocka6 shocka7 shocka8 30 | $frame shocka9 shocka10 31 | 32 | $frame shockb1 shockb2 shockb3 shockb4 shockb5 shockb6 33 | 34 | $frame shockc1 shockc2 shockc3 shockc4 shockc5 shockc6 shockc7 shockc8 35 | $frame shockc9 shockc10 36 | 37 | 38 | void(vector p) boss_missile; 39 | 40 | void() boss_face = 41 | { 42 | 43 | // go for another player if multi player 44 | if (self.enemy.health <= 0 || random() < 0.02) 45 | { 46 | self.enemy = find(self.enemy, classname, "player"); 47 | if (!self.enemy) 48 | self.enemy = find(self.enemy, classname, "player"); 49 | } 50 | ai_face(); 51 | }; 52 | 53 | void() boss_rise1 =[ $rise1, boss_rise2 ] { 54 | sound (self, CHAN_WEAPON, "boss1/out1.wav", 1, ATTN_NORM); 55 | }; 56 | void() boss_rise2 =[ $rise2, boss_rise3 ] { 57 | sound (self, CHAN_VOICE, "boss1/sight1.wav", 1, ATTN_NORM); 58 | }; 59 | void() boss_rise3 =[ $rise3, boss_rise4 ] {}; 60 | void() boss_rise4 =[ $rise4, boss_rise5 ] {}; 61 | void() boss_rise5 =[ $rise5, boss_rise6 ] {}; 62 | void() boss_rise6 =[ $rise6, boss_rise7 ] {}; 63 | void() boss_rise7 =[ $rise7, boss_rise8 ] {}; 64 | void() boss_rise8 =[ $rise8, boss_rise9 ] {}; 65 | void() boss_rise9 =[ $rise9, boss_rise10 ] {}; 66 | void() boss_rise10 =[ $rise10, boss_rise11 ] {}; 67 | void() boss_rise11 =[ $rise11, boss_rise12 ] {}; 68 | void() boss_rise12 =[ $rise12, boss_rise13 ] {}; 69 | void() boss_rise13 =[ $rise13, boss_rise14 ] {}; 70 | void() boss_rise14 =[ $rise14, boss_rise15 ] {}; 71 | void() boss_rise15 =[ $rise15, boss_rise16 ] {}; 72 | void() boss_rise16 =[ $rise16, boss_rise17 ] {}; 73 | void() boss_rise17 =[ $rise17, boss_missile1 ] {}; 74 | 75 | void() boss_idle1 =[ $walk1, boss_idle2 ] 76 | { 77 | // look for other players 78 | }; 79 | void() boss_idle2 =[ $walk2, boss_idle3 ] {boss_face();}; 80 | void() boss_idle3 =[ $walk3, boss_idle4 ] {boss_face();}; 81 | void() boss_idle4 =[ $walk4, boss_idle5 ] {boss_face();}; 82 | void() boss_idle5 =[ $walk5, boss_idle6 ] {boss_face();}; 83 | void() boss_idle6 =[ $walk6, boss_idle7 ] {boss_face();}; 84 | void() boss_idle7 =[ $walk7, boss_idle8 ] {boss_face();}; 85 | void() boss_idle8 =[ $walk8, boss_idle9 ] {boss_face();}; 86 | void() boss_idle9 =[ $walk9, boss_idle10 ] {boss_face();}; 87 | void() boss_idle10 =[ $walk10, boss_idle11 ] {boss_face();}; 88 | void() boss_idle11 =[ $walk11, boss_idle12 ] {boss_face();}; 89 | void() boss_idle12 =[ $walk12, boss_idle13 ] {boss_face();}; 90 | void() boss_idle13 =[ $walk13, boss_idle14 ] {boss_face();}; 91 | void() boss_idle14 =[ $walk14, boss_idle15 ] {boss_face();}; 92 | void() boss_idle15 =[ $walk15, boss_idle16 ] {boss_face();}; 93 | void() boss_idle16 =[ $walk16, boss_idle17 ] {boss_face();}; 94 | void() boss_idle17 =[ $walk17, boss_idle18 ] {boss_face();}; 95 | void() boss_idle18 =[ $walk18, boss_idle19 ] {boss_face();}; 96 | void() boss_idle19 =[ $walk19, boss_idle20 ] {boss_face();}; 97 | void() boss_idle20 =[ $walk20, boss_idle21 ] {boss_face();}; 98 | void() boss_idle21 =[ $walk21, boss_idle22 ] {boss_face();}; 99 | void() boss_idle22 =[ $walk22, boss_idle23 ] {boss_face();}; 100 | void() boss_idle23 =[ $walk23, boss_idle24 ] {boss_face();}; 101 | void() boss_idle24 =[ $walk24, boss_idle25 ] {boss_face();}; 102 | void() boss_idle25 =[ $walk25, boss_idle26 ] {boss_face();}; 103 | void() boss_idle26 =[ $walk26, boss_idle27 ] {boss_face();}; 104 | void() boss_idle27 =[ $walk27, boss_idle28 ] {boss_face();}; 105 | void() boss_idle28 =[ $walk28, boss_idle29 ] {boss_face();}; 106 | void() boss_idle29 =[ $walk29, boss_idle30 ] {boss_face();}; 107 | void() boss_idle30 =[ $walk30, boss_idle31 ] {boss_face();}; 108 | void() boss_idle31 =[ $walk31, boss_idle1 ] {boss_face();}; 109 | 110 | void() boss_missile1 =[ $attack1, boss_missile2 ] {boss_face();}; 111 | void() boss_missile2 =[ $attack2, boss_missile3 ] {boss_face();}; 112 | void() boss_missile3 =[ $attack3, boss_missile4 ] {boss_face();}; 113 | void() boss_missile4 =[ $attack4, boss_missile5 ] {boss_face();}; 114 | void() boss_missile5 =[ $attack5, boss_missile6 ] {boss_face();}; 115 | void() boss_missile6 =[ $attack6, boss_missile7 ] {boss_face();}; 116 | void() boss_missile7 =[ $attack7, boss_missile8 ] {boss_face();}; 117 | void() boss_missile8 =[ $attack8, boss_missile9 ] {boss_face();}; 118 | void() boss_missile9 =[ $attack9, boss_missile10 ] {boss_missile('100 100 200');}; 119 | void() boss_missile10 =[ $attack10, boss_missile11 ] {boss_face();}; 120 | void() boss_missile11 =[ $attack11, boss_missile12 ] {boss_face();}; 121 | void() boss_missile12 =[ $attack12, boss_missile13 ] {boss_face();}; 122 | void() boss_missile13 =[ $attack13, boss_missile14 ] {boss_face();}; 123 | void() boss_missile14 =[ $attack14, boss_missile15 ] {boss_face();}; 124 | void() boss_missile15 =[ $attack15, boss_missile16 ] {boss_face();}; 125 | void() boss_missile16 =[ $attack16, boss_missile17 ] {boss_face();}; 126 | void() boss_missile17 =[ $attack17, boss_missile18 ] {boss_face();}; 127 | void() boss_missile18 =[ $attack18, boss_missile19 ] {boss_face();}; 128 | void() boss_missile19 =[ $attack19, boss_missile20 ] {boss_face();}; 129 | void() boss_missile20 =[ $attack20, boss_missile21 ] {boss_missile('100 -100 200');}; 130 | void() boss_missile21 =[ $attack21, boss_missile22 ] {boss_face();}; 131 | void() boss_missile22 =[ $attack22, boss_missile23 ] {boss_face();}; 132 | void() boss_missile23 =[ $attack23, boss_missile1 ] {boss_face();}; 133 | 134 | void() boss_shocka1 =[ $shocka1, boss_shocka2 ] {}; 135 | void() boss_shocka2 =[ $shocka2, boss_shocka3 ] {}; 136 | void() boss_shocka3 =[ $shocka3, boss_shocka4 ] {}; 137 | void() boss_shocka4 =[ $shocka4, boss_shocka5 ] {}; 138 | void() boss_shocka5 =[ $shocka5, boss_shocka6 ] {}; 139 | void() boss_shocka6 =[ $shocka6, boss_shocka7 ] {}; 140 | void() boss_shocka7 =[ $shocka7, boss_shocka8 ] {}; 141 | void() boss_shocka8 =[ $shocka8, boss_shocka9 ] {}; 142 | void() boss_shocka9 =[ $shocka9, boss_shocka10 ] {}; 143 | void() boss_shocka10 =[ $shocka10, boss_missile1 ] {}; 144 | 145 | void() boss_shockb1 =[ $shockb1, boss_shockb2 ] {}; 146 | void() boss_shockb2 =[ $shockb2, boss_shockb3 ] {}; 147 | void() boss_shockb3 =[ $shockb3, boss_shockb4 ] {}; 148 | void() boss_shockb4 =[ $shockb4, boss_shockb5 ] {}; 149 | void() boss_shockb5 =[ $shockb5, boss_shockb6 ] {}; 150 | void() boss_shockb6 =[ $shockb6, boss_shockb7 ] {}; 151 | void() boss_shockb7 =[ $shockb1, boss_shockb8 ] {}; 152 | void() boss_shockb8 =[ $shockb2, boss_shockb9 ] {}; 153 | void() boss_shockb9 =[ $shockb3, boss_shockb10 ] {}; 154 | void() boss_shockb10 =[ $shockb4, boss_missile1 ] {}; 155 | 156 | void() boss_shockc1 =[ $shockc1, boss_shockc2 ] {}; 157 | void() boss_shockc2 =[ $shockc2, boss_shockc3 ] {}; 158 | void() boss_shockc3 =[ $shockc3, boss_shockc4 ] {}; 159 | void() boss_shockc4 =[ $shockc4, boss_shockc5 ] {}; 160 | void() boss_shockc5 =[ $shockc5, boss_shockc6 ] {}; 161 | void() boss_shockc6 =[ $shockc6, boss_shockc7 ] {}; 162 | void() boss_shockc7 =[ $shockc7, boss_shockc8 ] {}; 163 | void() boss_shockc8 =[ $shockc8, boss_shockc9 ] {}; 164 | void() boss_shockc9 =[ $shockc9, boss_shockc10 ] {}; 165 | void() boss_shockc10 =[ $shockc10, boss_death1 ] {}; 166 | 167 | void() boss_death1 = [$death1, boss_death2] { 168 | sound (self, CHAN_VOICE, "boss1/death.wav", 1, ATTN_NORM); 169 | }; 170 | void() boss_death2 = [$death2, boss_death3] {}; 171 | void() boss_death3 = [$death3, boss_death4] {}; 172 | void() boss_death4 = [$death4, boss_death5] {}; 173 | void() boss_death5 = [$death5, boss_death6] {}; 174 | void() boss_death6 = [$death6, boss_death7] {}; 175 | void() boss_death7 = [$death7, boss_death8] {}; 176 | void() boss_death8 = [$death8, boss_death9] {}; 177 | void() boss_death9 = [$death9, boss_death10] 178 | { 179 | sound (self, CHAN_BODY, "boss1/out1.wav", 1, ATTN_NORM); 180 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 181 | WriteByte (MSG_BROADCAST, TE_LAVASPLASH); 182 | WriteCoord (MSG_BROADCAST, self.origin_x); 183 | WriteCoord (MSG_BROADCAST, self.origin_y); 184 | WriteCoord (MSG_BROADCAST, self.origin_z); 185 | }; 186 | 187 | void() boss_death10 = [$death9, boss_death10] 188 | { 189 | killed_monsters = killed_monsters + 1; 190 | WriteByte (MSG_ALL, SVC_KILLEDMONSTER); // FIXME: reliable broadcast 191 | SUB_UseTargets (); 192 | remove (self); 193 | }; 194 | 195 | void(vector p) boss_missile = 196 | { 197 | local vector offang; 198 | local vector org, vec, d; 199 | local float t; 200 | 201 | offang = vectoangles (self.enemy.origin - self.origin); 202 | makevectors (offang); 203 | 204 | org = self.origin + p_x*v_forward + p_y*v_right + p_z*'0 0 1'; 205 | 206 | // lead the player on hard mode 207 | if (skill > 1) 208 | { 209 | t = vlen(self.enemy.origin - org) / 300; 210 | vec = self.enemy.velocity; 211 | vec_z = 0; 212 | d = self.enemy.origin + t * vec; 213 | } 214 | else 215 | { 216 | d = self.enemy.origin; 217 | } 218 | 219 | vec = normalize (d - org); 220 | 221 | launch_spike (org, vec); 222 | setmodel (newmis, "progs/lavaball.mdl"); 223 | newmis.avelocity = '200 100 300'; 224 | setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); 225 | newmis.velocity = vec*300; 226 | newmis.touch = T_MissileTouch; // rocket explosion 227 | sound (self, CHAN_WEAPON, "boss1/throw.wav", 1, ATTN_NORM); 228 | 229 | // check for dead enemy 230 | if (self.enemy.health <= 0) 231 | boss_idle1 (); 232 | }; 233 | 234 | 235 | void() boss_awake = 236 | { 237 | self.solid = SOLID_SLIDEBOX; 238 | self.movetype = MOVETYPE_STEP; 239 | self.takedamage = DAMAGE_NO; 240 | 241 | setmodel (self, "progs/boss.mdl"); 242 | setsize (self, '-128 -128 -24', '128 128 256'); 243 | 244 | if (skill == 0) 245 | self.health = 1; 246 | else 247 | self.health = 3; 248 | 249 | self.enemy = activator; 250 | 251 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 252 | WriteByte (MSG_BROADCAST, TE_LAVASPLASH); 253 | WriteCoord (MSG_BROADCAST, self.origin_x); 254 | WriteCoord (MSG_BROADCAST, self.origin_y); 255 | WriteCoord (MSG_BROADCAST, self.origin_z); 256 | 257 | self.yaw_speed = 20; 258 | boss_rise1 (); 259 | }; 260 | 261 | 262 | /*QUAKED monster_boss (1 0 0) (-128 -128 -24) (128 128 256) 263 | */ 264 | void() monster_boss = 265 | { 266 | if (deathmatch) 267 | { 268 | remove(self); 269 | return; 270 | } 271 | precache_model ("progs/boss.mdl"); 272 | precache_model ("progs/lavaball.mdl"); 273 | 274 | precache_sound ("weapons/rocket1i.wav"); 275 | precache_sound ("boss1/out1.wav"); 276 | precache_sound ("boss1/sight1.wav"); 277 | precache_sound ("misc/power.wav"); 278 | precache_sound ("boss1/throw.wav"); 279 | precache_sound ("boss1/pain.wav"); 280 | precache_sound ("boss1/death.wav"); 281 | 282 | total_monsters = total_monsters + 1; 283 | 284 | self.flags = self.flags | FL_MONSTER; // 1998-09-16 No exclusive deathmessage for being killed by Chthon fix by Maddes 285 | 286 | self.touch = monster_touch; // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten 287 | 288 | self.use = boss_awake; 289 | }; 290 | 291 | //=========================================================================== 292 | 293 | entity le1, le2; 294 | float lightning_end; 295 | 296 | void() lightning_fire = 297 | { 298 | local vector p1, p2; 299 | 300 | if (time >= lightning_end) 301 | { // done here, put the terminals back up 302 | self = le1; 303 | door_go_down (); 304 | self = le2; 305 | door_go_down (); 306 | return; 307 | } 308 | 309 | p1 = (le1.mins + le1.maxs) * 0.5; 310 | p1_z = le1.absmin_z - 16; 311 | 312 | p2 = (le2.mins + le2.maxs) * 0.5; 313 | p2_z = le2.absmin_z - 16; 314 | 315 | // compensate for length of bolt 316 | p2 = p2 - normalize(p2-p1)*100; 317 | 318 | self.nextthink = time + 0.1; 319 | self.think = lightning_fire; 320 | 321 | WriteByte (MSG_ALL, SVC_TEMPENTITY); 322 | WriteByte (MSG_ALL, TE_LIGHTNING3); 323 | WriteEntity (MSG_ALL, world); 324 | WriteCoord (MSG_ALL, p1_x); 325 | WriteCoord (MSG_ALL, p1_y); 326 | WriteCoord (MSG_ALL, p1_z); 327 | WriteCoord (MSG_ALL, p2_x); 328 | WriteCoord (MSG_ALL, p2_y); 329 | WriteCoord (MSG_ALL, p2_z); 330 | }; 331 | 332 | void() lightning_use = 333 | { 334 | if (lightning_end >= time + 1) 335 | return; 336 | 337 | le1 = find( world, target, "lightning"); 338 | le2 = find( le1, target, "lightning"); 339 | if (!le1 || !le2) 340 | { 341 | dprint ("missing lightning targets\n"); 342 | return; 343 | } 344 | 345 | if ( 346 | (le1.state != STATE_TOP && le1.state != STATE_BOTTOM) 347 | || (le2.state != STATE_TOP && le2.state != STATE_BOTTOM) 348 | || (le1.state != le2.state) ) 349 | { 350 | // dprint ("not aligned\n"); 351 | return; 352 | } 353 | 354 | // don't let the electrodes go back up until the bolt is done 355 | le1.nextthink = -1; 356 | le2.nextthink = -1; 357 | lightning_end = time + 1; 358 | 359 | sound (self, CHAN_VOICE, "misc/power.wav", 1, ATTN_NORM); 360 | lightning_fire (); 361 | 362 | // advance the boss pain if down 363 | self = find (world, classname, "monster_boss"); 364 | if (!self) 365 | return; 366 | self.enemy = activator; 367 | if (le1.state == STATE_TOP && self.health > 0) 368 | { 369 | sound (self, CHAN_VOICE, "boss1/pain.wav", 1, ATTN_NORM); 370 | self.health = self.health - 1; 371 | if (self.health >= 2) 372 | boss_shocka1(); 373 | else if (self.health == 1) 374 | boss_shockb1(); 375 | else if (self.health == 0) 376 | boss_shockc1(); 377 | } 378 | }; 379 | 380 | 381 | /*QUAKED event_lightning (0 1 1) (-16 -16 -16) (16 16 16) 382 | Just for boss level. 383 | */ 384 | void() event_lightning = 385 | { 386 | self.use = lightning_use; 387 | }; 388 | -------------------------------------------------------------------------------- /src/buttons.qc: -------------------------------------------------------------------------------- 1 | // button and multiple button 2 | 3 | void() button_wait; 4 | void() button_return; 5 | 6 | void() button_wait = 7 | { 8 | self.state = STATE_TOP; 9 | self.nextthink = self.ltime + self.wait; 10 | self.think = button_return; 11 | activator = self.enemy; 12 | SUB_UseTargets(); 13 | self.frame = 1; // use alternate textures 14 | }; 15 | 16 | void() button_done = 17 | { 18 | self.state = STATE_BOTTOM; 19 | }; 20 | 21 | void() button_return = 22 | { 23 | self.state = STATE_DOWN; 24 | SUB_CalcMove (self.pos1, self.speed, button_done); 25 | self.frame = 0; // use normal textures 26 | if (self.health) 27 | self.takedamage = DAMAGE_YES; // can be shot again 28 | }; 29 | 30 | 31 | void() button_blocked = 32 | { // do nothing, just don't ome all the way back out 33 | }; 34 | 35 | 36 | void() button_fire = 37 | { 38 | if (self.state == STATE_UP || self.state == STATE_TOP) 39 | return; 40 | 41 | sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); 42 | 43 | self.state = STATE_UP; 44 | SUB_CalcMove (self.pos2, self.speed, button_wait); 45 | }; 46 | 47 | 48 | void() button_use = 49 | { 50 | self.enemy = activator; 51 | button_fire (); 52 | }; 53 | 54 | void() button_touch = 55 | { 56 | if (other.classname != "player") 57 | return; 58 | self.enemy = other; 59 | button_fire (); 60 | }; 61 | 62 | void() button_killed = 63 | { 64 | self.enemy = damage_attacker; 65 | self.health = self.max_health; 66 | self.takedamage = DAMAGE_NO; // wil be reset upon return 67 | button_fire (); 68 | }; 69 | 70 | 71 | /*QUAKED func_button (0 .5 .8) ? 72 | When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. 73 | 74 | "angle" determines the opening direction 75 | "target" all entities with a matching targetname will be used 76 | "speed" override the default 40 speed 77 | "wait" override the default 1 second wait (-1 = never return) 78 | "lip" override the default 4 pixel lip remaining at end of move 79 | "health" if set, the button must be killed instead of touched 80 | "sounds" 81 | 0) steam metal 82 | 1) wooden clunk 83 | 2) metallic click 84 | 3) in-out 85 | */ 86 | void() func_button = 87 | { 88 | 89 | if (self.sounds == 0) 90 | { 91 | precache_sound ("buttons/airbut1.wav"); 92 | self.noise = "buttons/airbut1.wav"; 93 | } 94 | if (self.sounds == 1) 95 | { 96 | precache_sound ("buttons/switch21.wav"); 97 | self.noise = "buttons/switch21.wav"; 98 | } 99 | if (self.sounds == 2) 100 | { 101 | precache_sound ("buttons/switch02.wav"); 102 | self.noise = "buttons/switch02.wav"; 103 | } 104 | if (self.sounds == 3) 105 | { 106 | precache_sound ("buttons/switch04.wav"); 107 | self.noise = "buttons/switch04.wav"; 108 | } 109 | 110 | SetMovedir (); 111 | 112 | self.movetype = MOVETYPE_PUSH; 113 | self.solid = SOLID_BSP; 114 | setmodel (self, self.model); 115 | 116 | self.blocked = button_blocked; 117 | self.use = button_use; 118 | 119 | if (self.health) 120 | { 121 | self.max_health = self.health; 122 | self.th_die = button_killed; 123 | self.takedamage = DAMAGE_YES; 124 | } 125 | else 126 | self.touch = button_touch; 127 | 128 | if (!self.speed) 129 | self.speed = 40; 130 | if (!self.wait) 131 | self.wait = 1; 132 | if (!self.lip) 133 | self.lip = 4; 134 | 135 | self.state = STATE_BOTTOM; 136 | 137 | self.pos1 = self.origin; 138 | self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); 139 | }; 140 | 141 | -------------------------------------------------------------------------------- /src/changelog.txt: -------------------------------------------------------------------------------- 1 | Speed Mapping progs (SMP) 2 | by dumptruck_ds 3 | 4 | Version 2024.2 5 | 6 | 2024/6/20 7 | 8 | * Fixed a bug that prevented some monsters from spawning 9 | * Added Single Player Instagib modes (spinstagib) 10 | Set your desired mode in the console: 11 | gib_all - instagib everyone 12 | gib_monsters - only monsters instagib 13 | gib_player - only players instagib 14 | gib_off - disables instagib 15 | You can also enable them on the command line: 16 | fraglimit 666 - instagib everyone 17 | fraglimit 333 - only monsters instagib 18 | fraglimit 999 - only players instagib 19 | fraglimit 0 - disables instagib 20 | * instagib sounds will randomize between appropriate built-in sounds 21 | 22 | Version 2024.1 23 | 24 | 2024/3/18 25 | 26 | * This version was supposed to fix a bug that prevented monsters from spawning. 27 | The code was incorrect and is fixed in Version 2024.2 28 | 29 | Version 2023.1 30 | 31 | 2023/11/11 32 | * Added targets2-4 33 | * Added killtarget2 34 | * Added SPAWN_ANGRY for trigger spawned monsters 35 | * Added misc_model entity 36 | * Fixed armor taking damage when Pentagram of Protection is active 37 | * Fixed kill count if trigger spawned monsters have no targetnames 38 | * Added play_sound_triggered, play_sound_random and ambient_general 39 | 40 | Version 2021.11 41 | 42 | 2021/09/01 43 | * Fixed misc_fireball typo that lead to crash 44 | * Added KNOWN ISSUES information in smp_readme.txt 45 | 46 | Version 2021.10 47 | 48 | 2021/08/31 49 | * Completed all Quake Info Pool bug fixes 50 | * Fixed bug where trigger spawned items could fall out of level 51 | (items.qc thanks to TheSolopsist) 52 | * Fixed wrong obituary messages fix (multiple qc files) 53 | * Fixed Pentagram telefrag (combat.qc) 54 | * Fixed weapon items never firing their targets in coop or deathmatch 2 55 | (items.qc thanks to iw) 56 | * Fixed key items firing their targets multiple times in coop 57 | (items.qc thanks to iw) 58 | * Added is_waiting key to trigger_once and trigger_multiple 59 | a.k.a. dormant triggers (triggers.qc, defs.qc) 60 | * Fixed Shambler non-solid bug (shambler.qc) 61 | * Fixed Nailgun viewmodel projectile position (weapons.qc) 62 | * Fixed (rare) Rune restart bug (world.qc, client.qc) 63 | * Fixed misc_explobox bug (misc.qc from URQP) 64 | * Fixed sliding/not-jumping on monsters/boxes/players (monsters.qc) 65 | * Fixed drowning hurts armor (combat.qc, client.qc) 66 | * Added info_player_start2 spawnflag to trigger_changelevel. This replaces 67 | "Rune hacks". Useful for start and hub levels. 68 | 69 | Version 2021.9 70 | 71 | 2021/8/13 72 | * Fixed syntax errors in health_touch that broke Megahealth 73 | * Fixed Megahealth Rot courtesy of Quake Info Pool (items.qc) 74 | 75 | Version 2021.8 76 | 77 | 2021/8/11 78 | * Added custom gravity setting to worldspawn (def.qc, world.qc) 79 | * Items, artifacts, ammo, health and weapons can be suspended in air (items.qc) 80 | * Updated .fgd with new spawnflags and key / values 81 | * Updated readme with more detailed info on spawnflags and key / values 82 | 83 | Version 2021.7 84 | 85 | 2021/6/23 86 | * Added wait key to items in fgd 87 | * minor changes to readme 88 | 89 | Version 2021.6 90 | 91 | 2021/4/26 92 | * Items, Artifacts, Ammo and Weapons can be trigger spawned 93 | and set to respawn using the wait key. They can also be set 94 | to spawn and respawn silently or with teleport effects. 95 | (items.qc) 96 | * Added spawn silently flag to monsters (monsters.qc) 97 | 98 | Version 2021.5 99 | 100 | 2021/4/24 101 | * Fixed bug in trigger_hurt (defs.qc, triggers.qc) 102 | * Fixed glowing corpse bug (player.qc) 103 | * Fixed fraglimit / timelimit bug (client.qc) 104 | * Disabled suicide during intermission in multiplayer (client.qc) 105 | * Made fish gibbable (fish.qc) 106 | * Fixed bug where players respawned where they died (client.qc) 107 | * Fixed teamplay 1 bugs (combat.qc) 108 | * Fixed player not spawning bubbles when hurt in slime (player.qc) 109 | * Fixed bubble spawner bugs (player.qc) 110 | * Fixed constantly checking all impulses (weapons.qc) 111 | 112 | Version 2021.4 113 | 114 | 2021/4/22 115 | * Fixed typo in trigger_changelevel (client.qc) 116 | * New Episode Fix (eliminates need for start.bsp rune hack) (client.qc) 117 | * Fixed auto switching to Thunderbolt underwater (items.qc) 118 | * Disabled NoExit in SinglePlayer and Coop (client.qc) 119 | * Fixed player having velocity when respawning (client.qc) 120 | 121 | Version 2021.3 122 | 123 | 2021/4/17 124 | * Renamed a file to spawn.qc 125 | * Renamed a monster to monster_spawn 126 | * Added monster_spawn to smp.fgd 127 | 128 | Version 2021.2 129 | 130 | 2021/4/14 131 | * Fixed FTEQCC compiler warnings courtesy of Khreathor's mod_jam_progs 132 | 133 | Version 2021.1 134 | 135 | 2021/4/8 136 | * Fixed fish count bug (monsters.qc) 137 | * Fixed fish collision timing (fish.qc) 138 | * Fixed door unlock sound (doors.qc) 139 | * Added Preach's monster teleporting flag 140 | (spawnflag 8 to trigger spawn a targeted monster) 141 | * Added "trigger spawn" spawnflag to smp.fgd 142 | -------------------------------------------------------------------------------- /src/combat.qc: -------------------------------------------------------------------------------- 1 | 2 | void() T_MissileTouch; 3 | void() info_player_start; 4 | void(entity targ, entity attacker) ClientObituary; 5 | 6 | void() monster_death_use; 7 | 8 | //============================================================================ 9 | 10 | /* 11 | ============ 12 | CanDamage 13 | 14 | Returns true if the inflictor can directly damage the target. Used for 15 | explosions and melee attacks. 16 | ============ 17 | */ 18 | float(entity targ, entity inflictor) CanDamage = 19 | { 20 | // bmodels need special checking because their origin is 0,0,0 21 | if (targ.movetype == MOVETYPE_PUSH) 22 | { 23 | traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self); 24 | if (trace_fraction == 1) 25 | return TRUE; 26 | if (trace_ent == targ) 27 | return TRUE; 28 | return FALSE; 29 | } 30 | 31 | traceline(inflictor.origin, targ.origin, TRUE, self); 32 | if (trace_fraction == 1) 33 | return TRUE; 34 | traceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self); 35 | if (trace_fraction == 1) 36 | return TRUE; 37 | traceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self); 38 | if (trace_fraction == 1) 39 | return TRUE; 40 | traceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self); 41 | if (trace_fraction == 1) 42 | return TRUE; 43 | traceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self); 44 | if (trace_fraction == 1) 45 | return TRUE; 46 | 47 | return FALSE; 48 | }; 49 | 50 | 51 | /* 52 | ============ 53 | Killed 54 | ============ 55 | */ 56 | void(entity targ, entity attacker) Killed = 57 | { 58 | local entity oself; 59 | 60 | oself = self; 61 | self = targ; 62 | 63 | if (self.health < -99) 64 | self.health = -99; // don't let sbar look bad if a player 65 | 66 | if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE) 67 | { // doors, triggers, etc 68 | self.th_die (); 69 | self = oself; 70 | return; 71 | } 72 | 73 | self.enemy = attacker; 74 | 75 | // bump the monster counter 76 | if (self.flags & FL_MONSTER) 77 | { 78 | killed_monsters = killed_monsters + 1; 79 | WriteByte (MSG_ALL, SVC_KILLEDMONSTER); 80 | } 81 | 82 | ClientObituary(self, attacker); 83 | 84 | self.takedamage = DAMAGE_NO; 85 | self.touch = SUB_Null; 86 | 87 | monster_death_use(); 88 | self.th_die (); 89 | 90 | self = oself; 91 | }; 92 | 93 | 94 | /* 95 | ============ 96 | T_Damage 97 | 98 | The damage is coming from inflictor, but get mad at attacker 99 | This should be the only function that ever reduces health. 100 | ============ 101 | */ 102 | void(entity targ, entity inflictor, entity attacker, float damage) T_Damage= 103 | { 104 | local vector dir; 105 | local entity oldself; 106 | local float save; 107 | local float take; 108 | local float ignore_armor; //johnfitz 109 | local string death_type; //johnfitz 110 | 111 | if (!targ.takedamage) 112 | return; 113 | 114 | //johnfitz -- make sure targ.deathtype doesn't keep stale info after this function is done 115 | death_type = targ.deathtype; 116 | targ.deathtype = ""; 117 | //johnfitz 118 | 119 | // used by buttons and triggers to set activator for target firing 120 | damage_attacker = attacker; 121 | 122 | // team play damage avoidance 123 | // 1998-07-29 Teamplay 1 fix by Maddes start 124 | if ( (teamplay == 1) && (targ.team > 0) && (targ.team == attacker.team) 125 | && (targ != attacker) 126 | && (attacker.classname == "player") 127 | && (inflictor.classname != "door") ) // because squishing a teammate is still possible 128 | // 1998-07-29 Teamplay 1 fix by Maddes end 129 | return; 130 | 131 | // check for quad damage powerup on the attacker 132 | if (attacker.super_damage_finished > time) 133 | damage = damage * 4; 134 | 135 | // Instagib 136 | if (!deathmatch) 137 | { 138 | if ((cvar("fraglimit") == 666) && (!(attacker.classname == "fireball")) && (!(death_type == "falling" || death_type == "burning" || death_type == "drowning" || death_type == "squish" || death_type == "slimed" || targ.invincible_finished >= time || targ.flags & FL_GODMODE))) //everyone gibs 139 | damage = damage * 666; 140 | 141 | if ((cvar("fraglimit") == 333) && (attacker.flags & FL_CLIENT) && (!(attacker.classname == "fireball")) && (!(death_type == "falling" || death_type == "burning" || death_type == "drowning" || death_type == "squish" || death_type == "slimed" || targ.invincible_finished >= time || targ.flags & FL_GODMODE))) //gibs monsters only 142 | damage = damage * 666; 143 | 144 | if ((cvar("fraglimit") == 999) && (attacker.flags & FL_MONSTER) && (!(attacker.classname == "fireball")) && (!(death_type == "falling" || death_type == "burning" || death_type == "drowning" || death_type == "squish" || death_type == "slimed" || targ.invincible_finished >= time || targ.flags & FL_GODMODE))) //gibs players only 145 | damage = damage * 666; 146 | } 147 | 148 | //don't deplete armor if drowning, or protected by biosuit/pentagram/godmode (note: in ID1 pentagram/godmode doesn't actually protect your armor) 149 | if (death_type == "drowning" || targ.invincible_finished >= time || targ.flags & FL_GODMODE) 150 | ignore_armor = TRUE; 151 | else 152 | ignore_armor = FALSE; 153 | //johnfitz 154 | // save damage based on the target's armor level 155 | if (ignore_armor) //johnfitz -- some damage doesn't deplete armor 156 | { 157 | save = 0; 158 | } 159 | else 160 | { 161 | save = ceil(targ.armortype*damage); 162 | if (save >= targ.armorvalue) 163 | { 164 | save = targ.armorvalue; 165 | targ.armortype = 0; // lost all armor 166 | targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)); 167 | } 168 | } 169 | targ.armorvalue = targ.armorvalue - save; 170 | take = ceil(damage-save); 171 | 172 | // add to the damage total for clients, which will be sent as a single 173 | // message at the end of the frame 174 | // FIXME: remove after combining shotgun blasts? 175 | if (targ.flags & FL_CLIENT) 176 | { 177 | targ.dmg_take = targ.dmg_take + take; 178 | targ.dmg_save = targ.dmg_save + save; 179 | targ.dmg_inflictor = inflictor; 180 | } 181 | 182 | // figure momentum add 183 | if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) ) 184 | { 185 | dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5; 186 | dir = normalize(dir); 187 | targ.velocity = targ.velocity + dir*damage*8; 188 | } 189 | 190 | // check for godmode or invincibility 191 | if (targ.flags & FL_GODMODE) 192 | return; 193 | if (targ.invincible_finished >= time) 194 | { 195 | if (self.invincible_sound < time) 196 | { 197 | sound (targ, CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM); 198 | self.invincible_sound = time + 2; 199 | } 200 | return; 201 | } 202 | 203 | // do the damage 204 | targ.health = targ.health - take; 205 | 206 | if (targ.health <= 0) 207 | { 208 | Killed (targ, attacker); 209 | return; 210 | } 211 | 212 | // react to the damage 213 | oldself = self; 214 | self = targ; 215 | 216 | if ( (self.flags & FL_MONSTER) && attacker != world) 217 | { 218 | // get mad unless of the same class (except for soldiers) 219 | if (self != attacker && attacker != self.enemy) 220 | { 221 | if ( (self.classname != attacker.classname) 222 | || (self.classname == "monster_army" ) ) 223 | { 224 | if (self.enemy.classname == "player") 225 | self.oldenemy = self.enemy; 226 | self.enemy = attacker; 227 | FoundTarget (); 228 | } 229 | } 230 | } 231 | 232 | if (self.th_pain) 233 | { 234 | self.th_pain (attacker, take); 235 | // nightmare mode monsters don't go into pain frames often 236 | if (skill == 3) 237 | self.pain_finished = time + 5; 238 | } 239 | 240 | self = oldself; 241 | }; 242 | 243 | /* 244 | ============ 245 | T_RadiusDamage 246 | ============ 247 | */ 248 | // 1998-07-24 Wrong obituary messages fix by Zoid added parameter dtype 249 | void(entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage = 250 | { 251 | local float points; 252 | local entity head; 253 | local vector org; 254 | 255 | head = findradius(inflictor.origin, damage+40); 256 | 257 | while (head) 258 | { 259 | if (head != ignore) 260 | { 261 | if (head.takedamage) 262 | { 263 | org = head.origin + (head.mins + head.maxs)*0.5; 264 | points = 0.5*vlen (inflictor.origin - org); 265 | if (points < 0) 266 | points = 0; 267 | points = damage - points; 268 | if (head == attacker) 269 | points = points * 0.5; 270 | if (points > 0) 271 | { 272 | if (CanDamage (head, inflictor)) 273 | { // shambler takes half damage from all explosions 274 | head.deathtype = dtype; // 1998-07-24 Wrong obituary messages fix by Zoid 275 | if (head.classname == "monster_shambler") 276 | T_Damage (head, inflictor, attacker, points*0.5); 277 | else 278 | T_Damage (head, inflictor, attacker, points); 279 | } 280 | } 281 | } 282 | } 283 | head = head.chain; 284 | } 285 | }; 286 | 287 | /* 288 | ============ 289 | T_BeamDamage 290 | ============ 291 | */ 292 | void(entity attacker, float damage) T_BeamDamage = 293 | { 294 | local float points; 295 | local entity head; 296 | 297 | head = findradius(attacker.origin, damage+40); 298 | 299 | while (head) 300 | { 301 | if (head.takedamage) 302 | { 303 | points = 0.5*vlen (attacker.origin - head.origin); 304 | if (points < 0) 305 | points = 0; 306 | points = damage - points; 307 | if (head == attacker) 308 | points = points * 0.5; 309 | if (points > 0) 310 | { 311 | if (CanDamage (head, attacker)) 312 | { 313 | if (head.classname == "monster_shambler") 314 | T_Damage (head, attacker, attacker, points*0.5); 315 | else 316 | T_Damage (head, attacker, attacker, points); 317 | } 318 | } 319 | } 320 | head = head.chain; 321 | } 322 | }; 323 | -------------------------------------------------------------------------------- /src/demon.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | DEMON 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/demon3 10 | $scale 0.8 11 | $origin 0 0 24 12 | $base base 13 | $skin base 14 | 15 | $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9 16 | $frame stand10 stand11 stand12 stand13 17 | 18 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 19 | 20 | $frame run1 run2 run3 run4 run5 run6 21 | 22 | $frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9 leap10 23 | $frame leap11 leap12 24 | 25 | $frame pain1 pain2 pain3 pain4 pain5 pain6 26 | 27 | $frame death1 death2 death3 death4 death5 death6 death7 death8 death9 28 | 29 | $frame attacka1 attacka2 attacka3 attacka4 attacka5 attacka6 attacka7 attacka8 30 | $frame attacka9 attacka10 attacka11 attacka12 attacka13 attacka14 attacka15 31 | 32 | //============================================================================ 33 | 34 | void() Demon_JumpTouch; 35 | 36 | void() demon1_stand1 =[ $stand1, demon1_stand2 ] {ai_stand();}; 37 | void() demon1_stand2 =[ $stand2, demon1_stand3 ] {ai_stand();}; 38 | void() demon1_stand3 =[ $stand3, demon1_stand4 ] {ai_stand();}; 39 | void() demon1_stand4 =[ $stand4, demon1_stand5 ] {ai_stand();}; 40 | void() demon1_stand5 =[ $stand5, demon1_stand6 ] {ai_stand();}; 41 | void() demon1_stand6 =[ $stand6, demon1_stand7 ] {ai_stand();}; 42 | void() demon1_stand7 =[ $stand7, demon1_stand8 ] {ai_stand();}; 43 | void() demon1_stand8 =[ $stand8, demon1_stand9 ] {ai_stand();}; 44 | void() demon1_stand9 =[ $stand9, demon1_stand10 ] {ai_stand();}; 45 | void() demon1_stand10 =[ $stand10, demon1_stand11 ] {ai_stand();}; 46 | void() demon1_stand11 =[ $stand11, demon1_stand12 ] {ai_stand();}; 47 | void() demon1_stand12 =[ $stand12, demon1_stand13 ] {ai_stand();}; 48 | void() demon1_stand13 =[ $stand13, demon1_stand1 ] {ai_stand();}; 49 | 50 | void() demon1_walk1 =[ $walk1, demon1_walk2 ] { 51 | if (random() < 0.2) 52 | sound (self, CHAN_VOICE, "demon/idle1.wav", 1, ATTN_IDLE); 53 | ai_walk(8); 54 | }; 55 | void() demon1_walk2 =[ $walk2, demon1_walk3 ] {ai_walk(6);}; 56 | void() demon1_walk3 =[ $walk3, demon1_walk4 ] {ai_walk(6);}; 57 | void() demon1_walk4 =[ $walk4, demon1_walk5 ] {ai_walk(7);}; 58 | void() demon1_walk5 =[ $walk5, demon1_walk6 ] {ai_walk(4);}; 59 | void() demon1_walk6 =[ $walk6, demon1_walk7 ] {ai_walk(6);}; 60 | void() demon1_walk7 =[ $walk7, demon1_walk8 ] {ai_walk(10);}; 61 | void() demon1_walk8 =[ $walk8, demon1_walk1 ] {ai_walk(10);}; 62 | 63 | void() demon1_run1 =[ $run1, demon1_run2 ] { 64 | if (random() < 0.2) 65 | sound (self, CHAN_VOICE, "demon/idle1.wav", 1, ATTN_IDLE); 66 | ai_run(20);}; 67 | void() demon1_run2 =[ $run2, demon1_run3 ] {ai_run(15);}; 68 | void() demon1_run3 =[ $run3, demon1_run4 ] {ai_run(36);}; 69 | void() demon1_run4 =[ $run4, demon1_run5 ] {ai_run(20);}; 70 | void() demon1_run5 =[ $run5, demon1_run6 ] {ai_run(15);}; 71 | void() demon1_run6 =[ $run6, demon1_run1 ] {ai_run(36);}; 72 | 73 | void() demon1_jump1 =[ $leap1, demon1_jump2 ] {ai_face();}; 74 | void() demon1_jump2 =[ $leap2, demon1_jump3 ] {ai_face();}; 75 | void() demon1_jump3 =[ $leap3, demon1_jump4 ] {ai_face();}; 76 | void() demon1_jump4 =[ $leap4, demon1_jump5 ] 77 | { 78 | ai_face(); 79 | self.worldtype = 0; //fix for instakill bug -- dumptruck_ds 80 | self.touch = Demon_JumpTouch; 81 | makevectors (self.angles); 82 | self.origin_z = self.origin_z + 1; 83 | self.velocity = v_forward * 600 + '0 0 250'; 84 | if (self.flags & FL_ONGROUND) 85 | self.flags = self.flags - FL_ONGROUND; 86 | }; 87 | void() demon1_jump5 =[ $leap5, demon1_jump6 ] {}; 88 | void() demon1_jump6 =[ $leap6, demon1_jump7 ] {}; 89 | void() demon1_jump7 =[ $leap7, demon1_jump8 ] {}; 90 | void() demon1_jump8 =[ $leap8, demon1_jump9 ] {}; 91 | void() demon1_jump9 =[ $leap9, demon1_jump10 ] {}; 92 | void() demon1_jump10 =[ $leap10, demon1_jump1 ] { 93 | self.nextthink = time + 3; 94 | // if three seconds pass, assume demon is stuck and jump again 95 | }; 96 | 97 | void() demon1_jump11 =[ $leap11, demon1_jump12 ] {}; 98 | void() demon1_jump12 =[ $leap12, demon1_run1 ] {}; 99 | 100 | 101 | void() demon1_atta1 =[ $attacka1, demon1_atta2 ] {ai_charge(4);}; 102 | void() demon1_atta2 =[ $attacka2, demon1_atta3 ] {ai_charge(0);}; 103 | void() demon1_atta3 =[ $attacka3, demon1_atta4 ] {ai_charge(0);}; 104 | void() demon1_atta4 =[ $attacka4, demon1_atta5 ] {ai_charge(1);}; 105 | void() demon1_atta5 =[ $attacka5, demon1_atta6 ] {ai_charge(2); Demon_Melee(200);}; 106 | void() demon1_atta6 =[ $attacka6, demon1_atta7 ] {ai_charge(1);}; 107 | void() demon1_atta7 =[ $attacka7, demon1_atta8 ] {ai_charge(6);}; 108 | void() demon1_atta8 =[ $attacka8, demon1_atta9 ] {ai_charge(8);}; 109 | void() demon1_atta9 =[ $attacka9, demon1_atta10] {ai_charge(4);}; 110 | void() demon1_atta10 =[ $attacka10, demon1_atta11] {ai_charge(2);}; 111 | void() demon1_atta11 =[ $attacka11, demon1_atta12] {Demon_Melee(-200);}; 112 | void() demon1_atta12 =[ $attacka12, demon1_atta13] {ai_charge(5);}; 113 | void() demon1_atta13 =[ $attacka13, demon1_atta14] {ai_charge(8);}; 114 | void() demon1_atta14 =[ $attacka14, demon1_atta15] {ai_charge(4);}; 115 | void() demon1_atta15 =[ $attacka15, demon1_run1] {ai_charge(4);}; 116 | 117 | void() demon1_pain1 =[ $pain1, demon1_pain2 ] {}; 118 | void() demon1_pain2 =[ $pain2, demon1_pain3 ] {}; 119 | void() demon1_pain3 =[ $pain3, demon1_pain4 ] {}; 120 | void() demon1_pain4 =[ $pain4, demon1_pain5 ] {}; 121 | void() demon1_pain5 =[ $pain5, demon1_pain6 ] {}; 122 | void() demon1_pain6 =[ $pain6, demon1_run1 ] {}; 123 | 124 | void(entity attacker, float damage) demon1_pain = 125 | { 126 | if (self.touch == Demon_JumpTouch) 127 | return; 128 | 129 | if (self.pain_finished > time) 130 | return; 131 | 132 | self.pain_finished = time + 1; 133 | sound (self, CHAN_VOICE, "demon/dpain1.wav", 1, ATTN_NORM); 134 | 135 | if (random()*200 > damage) 136 | return; // didn't flinch 137 | 138 | demon1_pain1 (); 139 | }; 140 | 141 | void() demon1_die1 =[ $death1, demon1_die2 ] { 142 | sound (self, CHAN_VOICE, "demon/ddeath.wav", 1, ATTN_NORM);}; 143 | void() demon1_die2 =[ $death2, demon1_die3 ] {}; 144 | void() demon1_die3 =[ $death3, demon1_die4 ] {}; 145 | void() demon1_die4 =[ $death4, demon1_die5 ] {}; 146 | void() demon1_die5 =[ $death5, demon1_die6 ] {}; 147 | void() demon1_die6 =[ $death6, demon1_die7 ] 148 | {self.solid = SOLID_NOT;}; 149 | void() demon1_die7 =[ $death7, demon1_die8 ] {}; 150 | void() demon1_die8 =[ $death8, demon1_die9 ] {}; 151 | void() demon1_die9 =[ $death9, demon1_die9 ] {}; 152 | 153 | void() demon_die = 154 | { 155 | // check for gib 156 | if (self.health < -80) 157 | { 158 | ThrowHead ("progs/h_demon.mdl", self.health); 159 | ThrowGib ("progs/gib1.mdl", self.health); 160 | ThrowGib ("progs/gib1.mdl", self.health); 161 | ThrowGib ("progs/gib1.mdl", self.health); 162 | // spinstagib sounds 163 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 164 | { 165 | GibSoundsRandom(); 166 | return; 167 | } 168 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 169 | return; 170 | } 171 | 172 | // regular death 173 | demon1_die1 (); 174 | }; 175 | 176 | 177 | void() Demon_MeleeAttack = 178 | { 179 | demon1_atta1 (); 180 | }; 181 | 182 | 183 | /*QUAKED monster_demon1 (1 0 0) (-32 -32 -24) (32 32 64) Ambush 184 | 185 | */ 186 | void() monster_demon1 = 187 | { 188 | if (deathmatch) 189 | { 190 | remove(self); 191 | return; 192 | } 193 | precache_model ("progs/demon.mdl"); 194 | precache_model ("progs/h_demon.mdl"); 195 | 196 | precache_sound ("demon/ddeath.wav"); 197 | precache_sound ("demon/dhit2.wav"); 198 | precache_sound ("demon/djump.wav"); 199 | precache_sound ("demon/dpain1.wav"); 200 | precache_sound ("demon/idle1.wav"); 201 | precache_sound ("demon/sight2.wav"); 202 | 203 | self.solid = SOLID_SLIDEBOX; 204 | self.movetype = MOVETYPE_STEP; 205 | 206 | setmodel (self, "progs/demon.mdl"); 207 | 208 | setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); 209 | self.health = 300; 210 | 211 | self.th_stand = demon1_stand1; 212 | self.th_walk = demon1_walk1; 213 | self.th_run = demon1_run1; 214 | self.th_die = demon_die; 215 | self.th_melee = Demon_MeleeAttack; // one of two attacks 216 | self.th_missile = demon1_jump1; // jump attack 217 | self.th_pain = demon1_pain; 218 | 219 | walkmonster_start(); 220 | }; 221 | 222 | 223 | /* 224 | ============================================================================== 225 | 226 | DEMON 227 | 228 | ============================================================================== 229 | */ 230 | 231 | /* 232 | ============== 233 | CheckDemonMelee 234 | 235 | Returns TRUE if a melee attack would hit right now 236 | ============== 237 | */ 238 | float() CheckDemonMelee = 239 | { 240 | if (enemy_range == RANGE_MELEE) 241 | { // FIXME: check canreach 242 | self.attack_state = AS_MELEE; 243 | return TRUE; 244 | } 245 | return FALSE; 246 | }; 247 | 248 | /* 249 | ============== 250 | CheckDemonJump 251 | 252 | ============== 253 | */ 254 | float() CheckDemonJump = 255 | { 256 | local vector dist; 257 | local float d; 258 | 259 | if (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z 260 | + 0.75 * self.enemy.size_z) 261 | return FALSE; 262 | 263 | if (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z 264 | + 0.25 * self.enemy.size_z) 265 | return FALSE; 266 | 267 | dist = self.enemy.origin - self.origin; 268 | dist_z = 0; 269 | 270 | d = vlen(dist); 271 | 272 | if (d < 100) 273 | return FALSE; 274 | 275 | if (d > 200) 276 | { 277 | if (random() < 0.9) 278 | return FALSE; 279 | } 280 | 281 | return TRUE; 282 | }; 283 | 284 | float() DemonCheckAttack = 285 | { 286 | 287 | // if close enough for slashing, go for it 288 | if (CheckDemonMelee ()) 289 | { 290 | self.attack_state = AS_MELEE; 291 | return TRUE; 292 | } 293 | 294 | if (CheckDemonJump ()) 295 | { 296 | self.attack_state = AS_MISSILE; 297 | sound (self, CHAN_VOICE, "demon/djump.wav", 1, ATTN_NORM); 298 | return TRUE; 299 | } 300 | 301 | return FALSE; 302 | }; 303 | 304 | 305 | //=========================================================================== 306 | 307 | void(float side) Demon_Melee = 308 | { 309 | local float ldmg; 310 | local vector delta; 311 | 312 | ai_face (); 313 | walkmove (self.ideal_yaw, 12); // allow a little closing 314 | 315 | delta = self.enemy.origin - self.origin; 316 | 317 | if (vlen(delta) > 100) 318 | return; 319 | if (!CanDamage (self.enemy, self)) 320 | return; 321 | 322 | sound (self, CHAN_WEAPON, "demon/dhit2.wav", 1, ATTN_NORM); 323 | ldmg = 10 + 5*random(); 324 | T_Damage (self.enemy, self, self, ldmg); 325 | 326 | makevectors (self.angles); 327 | SpawnMeatSpray (self.origin + v_forward*16, side * v_right); 328 | }; 329 | 330 | 331 | void() Demon_JumpTouch = 332 | { 333 | local float ldmg; 334 | 335 | if (self.health <= 0) 336 | return; 337 | 338 | if (other.takedamage) 339 | { 340 | if ( vlen(self.velocity) > 400 ) 341 | { 342 | if !(self.worldtype) 343 | { 344 | ldmg = 40 + 10*random(); 345 | T_Damage (other, self, self, ldmg); 346 | if (other.health > 0) // is the player still alive? Preach's instakill bug check - dumptruck_ds 347 | self.worldtype = 1; 348 | } 349 | } 350 | } 351 | 352 | if (!checkbottom(self)) 353 | { 354 | if (self.flags & FL_ONGROUND) 355 | { // jump randomly to not get hung up 356 | //dprint ("popjump\n"); 357 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 358 | // self.touch = SUB_Null; 359 | self.touch = monster_touch; 360 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 361 | self.think = demon1_jump1; 362 | self.nextthink = time + 0.1; 363 | 364 | // self.velocity_x = (random() - 0.5) * 600; 365 | // self.velocity_y = (random() - 0.5) * 600; 366 | // self.velocity_z = 200; 367 | // self.flags = self.flags - FL_ONGROUND; 368 | } 369 | return; // not on ground yet 370 | } 371 | 372 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 373 | // self.touch = SUB_Null; 374 | self.touch = monster_touch; 375 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 376 | self.think = demon1_jump11; 377 | self.nextthink = time + 0.1; 378 | }; 379 | -------------------------------------------------------------------------------- /src/dog.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | DOG 5 | 6 | ============================================================================== 7 | */ 8 | $cd id1/models/dog 9 | $origin 0 0 24 10 | $base base 11 | $skin skin 12 | 13 | $frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8 14 | 15 | $frame death1 death2 death3 death4 death5 death6 death7 death8 death9 16 | 17 | $frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8 18 | $frame deathb9 19 | 20 | $frame pain1 pain2 pain3 pain4 pain5 pain6 21 | 22 | $frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10 23 | $frame painb11 painb12 painb13 painb14 painb15 painb16 24 | 25 | $frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12 26 | 27 | $frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9 28 | 29 | $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9 30 | 31 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 32 | 33 | 34 | void() dog_leap1; 35 | void() dog_run1; 36 | 37 | /* 38 | ================ 39 | dog_bite 40 | 41 | ================ 42 | */ 43 | void() dog_bite = 44 | { 45 | local vector delta; 46 | local float ldmg; 47 | 48 | if (!self.enemy) 49 | return; 50 | 51 | ai_charge(10); 52 | 53 | if (!CanDamage (self.enemy, self)) 54 | return; 55 | 56 | delta = self.enemy.origin - self.origin; 57 | 58 | if (vlen(delta) > 100) 59 | return; 60 | 61 | ldmg = (random() + random() + random()) * 8; 62 | T_Damage (self.enemy, self, self, ldmg); 63 | }; 64 | 65 | void() Dog_JumpTouch = 66 | { 67 | local float ldmg; 68 | 69 | if (self.health <= 0) 70 | return; 71 | 72 | if (other.takedamage) 73 | { 74 | if ( vlen(self.velocity) > 300 ) 75 | { 76 | if !(self.worldtype) 77 | { 78 | ldmg = 10 + 10*random(); 79 | T_Damage (other, self, self, ldmg); 80 | if (other.health > 0) // is the player still alive? Preach's instakill bug check - dumptruck_ds 81 | self.worldtype = 1; 82 | } 83 | } 84 | } 85 | 86 | if (!checkbottom(self)) 87 | { 88 | if (self.flags & FL_ONGROUND) 89 | { // jump randomly to not get hung up 90 | //dprint ("popjump\n"); 91 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 92 | // self.touch = SUB_Null; 93 | self.touch = monster_touch; 94 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 95 | self.think = dog_leap1; 96 | self.nextthink = time + 0.1; 97 | 98 | // self.velocity_x = (random() - 0.5) * 600; 99 | // self.velocity_y = (random() - 0.5) * 600; 100 | // self.velocity_z = 200; 101 | // self.flags = self.flags - FL_ONGROUND; 102 | } 103 | return; // not on ground yet 104 | } 105 | 106 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 107 | // self.touch = SUB_Null; 108 | self.touch = monster_touch; 109 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 110 | self.think = dog_run1; 111 | self.nextthink = time + 0.1; 112 | }; 113 | 114 | 115 | void() dog_stand1 =[ $stand1, dog_stand2 ] {ai_stand();}; 116 | void() dog_stand2 =[ $stand2, dog_stand3 ] {ai_stand();}; 117 | void() dog_stand3 =[ $stand3, dog_stand4 ] {ai_stand();}; 118 | void() dog_stand4 =[ $stand4, dog_stand5 ] {ai_stand();}; 119 | void() dog_stand5 =[ $stand5, dog_stand6 ] {ai_stand();}; 120 | void() dog_stand6 =[ $stand6, dog_stand7 ] {ai_stand();}; 121 | void() dog_stand7 =[ $stand7, dog_stand8 ] {ai_stand();}; 122 | void() dog_stand8 =[ $stand8, dog_stand9 ] {ai_stand();}; 123 | void() dog_stand9 =[ $stand9, dog_stand1 ] {ai_stand();}; 124 | 125 | void() dog_walk1 =[ $walk1 , dog_walk2 ] { 126 | if (random() < 0.2) 127 | sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE); 128 | ai_walk(8);}; 129 | void() dog_walk2 =[ $walk2 , dog_walk3 ] {ai_walk(8);}; 130 | void() dog_walk3 =[ $walk3 , dog_walk4 ] {ai_walk(8);}; 131 | void() dog_walk4 =[ $walk4 , dog_walk5 ] {ai_walk(8);}; 132 | void() dog_walk5 =[ $walk5 , dog_walk6 ] {ai_walk(8);}; 133 | void() dog_walk6 =[ $walk6 , dog_walk7 ] {ai_walk(8);}; 134 | void() dog_walk7 =[ $walk7 , dog_walk8 ] {ai_walk(8);}; 135 | void() dog_walk8 =[ $walk8 , dog_walk1 ] {ai_walk(8);}; 136 | 137 | void() dog_run1 =[ $run1 , dog_run2 ] { 138 | if (random() < 0.2) 139 | sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE); 140 | ai_run(16);}; 141 | void() dog_run2 =[ $run2 , dog_run3 ] {ai_run(32);}; 142 | void() dog_run3 =[ $run3 , dog_run4 ] {ai_run(32);}; 143 | void() dog_run4 =[ $run4 , dog_run5 ] {ai_run(20);}; 144 | void() dog_run5 =[ $run5 , dog_run6 ] {ai_run(64);}; 145 | void() dog_run6 =[ $run6 , dog_run7 ] {ai_run(32);}; 146 | void() dog_run7 =[ $run7 , dog_run8 ] {ai_run(16);}; 147 | void() dog_run8 =[ $run8 , dog_run9 ] {ai_run(32);}; 148 | void() dog_run9 =[ $run9 , dog_run10 ] {ai_run(32);}; 149 | void() dog_run10 =[ $run10 , dog_run11 ] {ai_run(20);}; 150 | void() dog_run11 =[ $run11 , dog_run12 ] {ai_run(64);}; 151 | void() dog_run12 =[ $run12 , dog_run1 ] {ai_run(32);}; 152 | 153 | void() dog_atta1 =[ $attack1, dog_atta2 ] {ai_charge(10);}; 154 | void() dog_atta2 =[ $attack2, dog_atta3 ] {ai_charge(10);}; 155 | void() dog_atta3 =[ $attack3, dog_atta4 ] {ai_charge(10);}; 156 | void() dog_atta4 =[ $attack4, dog_atta5 ] { 157 | sound (self, CHAN_VOICE, "dog/dattack1.wav", 1, ATTN_NORM); 158 | dog_bite();}; 159 | void() dog_atta5 =[ $attack5, dog_atta6 ] {ai_charge(10);}; 160 | void() dog_atta6 =[ $attack6, dog_atta7 ] {ai_charge(10);}; 161 | void() dog_atta7 =[ $attack7, dog_atta8 ] {ai_charge(10);}; 162 | void() dog_atta8 =[ $attack8, dog_run1 ] {ai_charge(10);}; 163 | 164 | void() dog_leap1 =[ $leap1, dog_leap2 ] {ai_face();}; 165 | void() dog_leap2 =[ $leap2, dog_leap3 ] 166 | { 167 | ai_face(); 168 | self.worldtype = 0; //fix for instakill bug -- dumptruck_ds 169 | self.touch = Dog_JumpTouch; 170 | makevectors (self.angles); 171 | self.origin_z = self.origin_z + 1; 172 | self.velocity = v_forward * 300 + '0 0 200'; 173 | if (self.flags & FL_ONGROUND) 174 | self.flags = self.flags - FL_ONGROUND; 175 | }; 176 | 177 | void() dog_leap3 =[ $leap3, dog_leap4 ] {}; 178 | void() dog_leap4 =[ $leap4, dog_leap5 ] {}; 179 | void() dog_leap5 =[ $leap5, dog_leap6 ] {}; 180 | void() dog_leap6 =[ $leap6, dog_leap7 ] {}; 181 | void() dog_leap7 =[ $leap7, dog_leap8 ] {}; 182 | void() dog_leap8 =[ $leap8, dog_leap9 ] {}; 183 | void() dog_leap9 =[ $leap9, dog_leap9 ] {}; 184 | 185 | void() dog_pain1 =[ $pain1 , dog_pain2 ] {}; 186 | void() dog_pain2 =[ $pain2 , dog_pain3 ] {}; 187 | void() dog_pain3 =[ $pain3 , dog_pain4 ] {}; 188 | void() dog_pain4 =[ $pain4 , dog_pain5 ] {}; 189 | void() dog_pain5 =[ $pain5 , dog_pain6 ] {}; 190 | void() dog_pain6 =[ $pain6 , dog_run1 ] {}; 191 | 192 | void() dog_painb1 =[ $painb1 , dog_painb2 ] {}; 193 | void() dog_painb2 =[ $painb2 , dog_painb3 ] {}; 194 | void() dog_painb3 =[ $painb3 , dog_painb4 ] {ai_pain(4);}; 195 | void() dog_painb4 =[ $painb4 , dog_painb5 ] {ai_pain(12);}; 196 | void() dog_painb5 =[ $painb5 , dog_painb6 ] {ai_pain(12);}; 197 | void() dog_painb6 =[ $painb6 , dog_painb7 ] {ai_pain(2);}; 198 | void() dog_painb7 =[ $painb7 , dog_painb8 ] {}; 199 | void() dog_painb8 =[ $painb8 , dog_painb9 ] {ai_pain(4);}; 200 | void() dog_painb9 =[ $painb9 , dog_painb10 ] {}; 201 | void() dog_painb10 =[ $painb10 , dog_painb11 ] {ai_pain(10);}; 202 | void() dog_painb11 =[ $painb11 , dog_painb12 ] {}; 203 | void() dog_painb12 =[ $painb12 , dog_painb13 ] {}; 204 | void() dog_painb13 =[ $painb13 , dog_painb14 ] {}; 205 | void() dog_painb14 =[ $painb14 , dog_painb15 ] {}; 206 | void() dog_painb15 =[ $painb15 , dog_painb16 ] {}; 207 | void() dog_painb16 =[ $painb16 , dog_run1 ] {}; 208 | 209 | void(entity attacker, float damage) dog_pain = // compiler warning fixes from mod_jam_progs -- dumptruck_ds 210 | { 211 | sound (self, CHAN_VOICE, "dog/dpain1.wav", 1, ATTN_NORM); 212 | 213 | if (random() > 0.5) 214 | dog_pain1 (); 215 | else 216 | dog_painb1 (); 217 | }; 218 | 219 | void() dog_die1 =[ $death1, dog_die2 ] {}; 220 | void() dog_die2 =[ $death2, dog_die3 ] {}; 221 | void() dog_die3 =[ $death3, dog_die4 ] {}; 222 | void() dog_die4 =[ $death4, dog_die5 ] {}; 223 | void() dog_die5 =[ $death5, dog_die6 ] {}; 224 | void() dog_die6 =[ $death6, dog_die7 ] {}; 225 | void() dog_die7 =[ $death7, dog_die8 ] {}; 226 | void() dog_die8 =[ $death8, dog_die9 ] {}; 227 | void() dog_die9 =[ $death9, dog_die9 ] {}; 228 | 229 | void() dog_dieb1 =[ $deathb1, dog_dieb2 ] {}; 230 | void() dog_dieb2 =[ $deathb2, dog_dieb3 ] {}; 231 | void() dog_dieb3 =[ $deathb3, dog_dieb4 ] {}; 232 | void() dog_dieb4 =[ $deathb4, dog_dieb5 ] {}; 233 | void() dog_dieb5 =[ $deathb5, dog_dieb6 ] {}; 234 | void() dog_dieb6 =[ $deathb6, dog_dieb7 ] {}; 235 | void() dog_dieb7 =[ $deathb7, dog_dieb8 ] {}; 236 | void() dog_dieb8 =[ $deathb8, dog_dieb9 ] {}; 237 | void() dog_dieb9 =[ $deathb9, dog_dieb9 ] {}; 238 | 239 | 240 | void() dog_die = 241 | { 242 | // check for gib 243 | if (self.health < -35) 244 | { 245 | ThrowGib ("progs/gib3.mdl", self.health); 246 | ThrowGib ("progs/gib3.mdl", self.health); 247 | ThrowGib ("progs/gib3.mdl", self.health); 248 | ThrowHead ("progs/h_dog.mdl", self.health); 249 | // spinstagib sounds 250 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 251 | { 252 | GibSoundsRandom(); 253 | return; 254 | } 255 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 256 | return; 257 | } 258 | 259 | // regular death 260 | sound (self, CHAN_VOICE, "dog/ddeath.wav", 1, ATTN_NORM); 261 | self.solid = SOLID_NOT; 262 | 263 | if (random() > 0.5) 264 | dog_die1 (); 265 | else 266 | dog_dieb1 (); 267 | }; 268 | 269 | //============================================================================ 270 | 271 | /* 272 | ============== 273 | CheckDogMelee 274 | 275 | Returns TRUE if a melee attack would hit right now 276 | ============== 277 | */ 278 | float() CheckDogMelee = 279 | { 280 | if (enemy_range == RANGE_MELEE) 281 | { // FIXME: check canreach 282 | self.attack_state = AS_MELEE; 283 | return TRUE; 284 | } 285 | return FALSE; 286 | }; 287 | 288 | /* 289 | ============== 290 | CheckDogJump 291 | 292 | ============== 293 | */ 294 | float() CheckDogJump = 295 | { 296 | local vector dist; 297 | local float d; 298 | 299 | if (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z 300 | + 0.75 * self.enemy.size_z) 301 | return FALSE; 302 | 303 | if (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z 304 | + 0.25 * self.enemy.size_z) 305 | return FALSE; 306 | 307 | dist = self.enemy.origin - self.origin; 308 | dist_z = 0; 309 | 310 | d = vlen(dist); 311 | 312 | if (d < 80) 313 | return FALSE; 314 | 315 | if (d > 150) 316 | return FALSE; 317 | 318 | return TRUE; 319 | }; 320 | 321 | float() DogCheckAttack = 322 | { 323 | 324 | // if close enough for slashing, go for it 325 | if (CheckDogMelee ()) 326 | { 327 | self.attack_state = AS_MELEE; 328 | return TRUE; 329 | } 330 | 331 | if (CheckDogJump ()) 332 | { 333 | self.attack_state = AS_MISSILE; 334 | return TRUE; 335 | } 336 | 337 | return FALSE; 338 | }; 339 | 340 | 341 | //=========================================================================== 342 | 343 | /*QUAKED monster_dog (1 0 0) (-32 -32 -24) (32 32 40) Ambush 344 | 345 | */ 346 | void() monster_dog = 347 | { 348 | if (deathmatch) 349 | { 350 | remove(self); 351 | return; 352 | } 353 | precache_model ("progs/h_dog.mdl"); 354 | precache_model ("progs/dog.mdl"); 355 | 356 | precache_sound ("dog/dattack1.wav"); 357 | precache_sound ("dog/ddeath.wav"); 358 | precache_sound ("dog/dpain1.wav"); 359 | precache_sound ("dog/dsight.wav"); 360 | precache_sound ("dog/idle.wav"); 361 | 362 | self.solid = SOLID_SLIDEBOX; 363 | self.movetype = MOVETYPE_STEP; 364 | 365 | setmodel (self, "progs/dog.mdl"); 366 | 367 | setsize (self, '-32 -32 -24', '32 32 40'); 368 | self.health = 25; 369 | 370 | self.th_stand = dog_stand1; 371 | self.th_walk = dog_walk1; 372 | self.th_run = dog_run1; 373 | self.th_pain = dog_pain; 374 | self.th_die = dog_die; 375 | self.th_melee = dog_atta1; 376 | self.th_missile = dog_leap1; 377 | 378 | walkmonster_start(); 379 | }; 380 | -------------------------------------------------------------------------------- /src/enforcer.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | SOLDIER / PLAYER 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/enforcer 10 | $origin 0 -6 24 11 | $base base 12 | $skin skin 13 | 14 | $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 15 | 16 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 17 | $frame walk11 walk12 walk13 walk14 walk15 walk16 18 | 19 | $frame run1 run2 run3 run4 run5 run6 run7 run8 20 | 21 | $frame attack1 attack2 attack3 attack4 attack5 attack6 22 | $frame attack7 attack8 attack9 attack10 23 | 24 | $frame death1 death2 death3 death4 death5 death6 death7 death8 25 | $frame death9 death10 death11 death12 death13 death14 26 | 27 | $frame fdeath1 fdeath2 fdeath3 fdeath4 fdeath5 fdeath6 fdeath7 fdeath8 28 | $frame fdeath9 fdeath10 fdeath11 29 | 30 | $frame paina1 paina2 paina3 paina4 31 | 32 | $frame painb1 painb2 painb3 painb4 painb5 33 | 34 | $frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 35 | 36 | $frame paind1 paind2 paind3 paind4 paind5 paind6 paind7 paind8 37 | $frame paind9 paind10 paind11 paind12 paind13 paind14 paind15 paind16 38 | $frame paind17 paind18 paind19 39 | 40 | 41 | void() Laser_Touch = 42 | { 43 | local vector org; 44 | if (other == self.owner) 45 | return; // don't explode on owner 46 | 47 | if (pointcontents(self.origin) == CONTENT_SKY) 48 | { 49 | remove(self); 50 | return; 51 | } 52 | 53 | sound (self, CHAN_WEAPON, "enforcer/enfstop.wav", 1, ATTN_STATIC); 54 | org = self.origin - 8*normalize(self.velocity); 55 | 56 | if (other.health) 57 | { 58 | SpawnBlood (org, self.velocity*0.2, 15); 59 | other.deathtype = "laser"; // 1998-07-24 Wrong obituary messages fix by Zoid 60 | T_Damage (other, self, self.owner, 15); 61 | } 62 | else 63 | { 64 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 65 | WriteByte (MSG_BROADCAST, TE_GUNSHOT); 66 | WriteCoord (MSG_BROADCAST, org_x); 67 | WriteCoord (MSG_BROADCAST, org_y); 68 | WriteCoord (MSG_BROADCAST, org_z); 69 | } 70 | 71 | remove(self); 72 | }; 73 | 74 | void(vector org, vector vec) LaunchLaser = 75 | { 76 | 77 | if (self.classname == "monster_enforcer") 78 | sound (self, CHAN_WEAPON, "enforcer/enfire.wav", 1, ATTN_NORM); 79 | 80 | vec = normalize(vec); 81 | 82 | newmis = spawn(); 83 | newmis.owner = self; 84 | newmis.movetype = MOVETYPE_FLY; 85 | newmis.solid = SOLID_BBOX; 86 | newmis.effects = EF_DIMLIGHT; 87 | 88 | setmodel (newmis, "progs/laser.mdl"); 89 | setsize (newmis, '0 0 0', '0 0 0'); 90 | 91 | setorigin (newmis, org); 92 | 93 | newmis.velocity = vec * 600; 94 | newmis.angles = vectoangles(newmis.velocity); 95 | 96 | newmis.nextthink = time + 5; 97 | newmis.think = SUB_Remove; 98 | newmis.touch = Laser_Touch; 99 | }; 100 | 101 | 102 | 103 | void() enforcer_fire = 104 | { 105 | local vector org; 106 | 107 | self.effects = self.effects | EF_MUZZLEFLASH; 108 | makevectors (self.angles); 109 | 110 | org = self.origin + v_forward * 30 + v_right * 8.5 + '0 0 16'; 111 | 112 | LaunchLaser(org, self.enemy.origin - self.origin); 113 | }; 114 | 115 | //============================================================================ 116 | 117 | void() enf_stand1 =[ $stand1, enf_stand2 ] {ai_stand();}; 118 | void() enf_stand2 =[ $stand2, enf_stand3 ] {ai_stand();}; 119 | void() enf_stand3 =[ $stand3, enf_stand4 ] {ai_stand();}; 120 | void() enf_stand4 =[ $stand4, enf_stand5 ] {ai_stand();}; 121 | void() enf_stand5 =[ $stand5, enf_stand6 ] {ai_stand();}; 122 | void() enf_stand6 =[ $stand6, enf_stand7 ] {ai_stand();}; 123 | void() enf_stand7 =[ $stand7, enf_stand1 ] {ai_stand();}; 124 | 125 | void() enf_walk1 =[ $walk1 , enf_walk2 ] { 126 | if (random() < 0.2) 127 | sound (self, CHAN_VOICE, "enforcer/idle1.wav", 1, ATTN_IDLE); 128 | ai_walk(2);}; 129 | void() enf_walk2 =[ $walk2 , enf_walk3 ] {ai_walk(4);}; 130 | void() enf_walk3 =[ $walk3 , enf_walk4 ] {ai_walk(4);}; 131 | void() enf_walk4 =[ $walk4 , enf_walk5 ] {ai_walk(3);}; 132 | void() enf_walk5 =[ $walk5 , enf_walk6 ] {ai_walk(1);}; 133 | void() enf_walk6 =[ $walk6 , enf_walk7 ] {ai_walk(2);}; 134 | void() enf_walk7 =[ $walk7 , enf_walk8 ] {ai_walk(2);}; 135 | void() enf_walk8 =[ $walk8 , enf_walk9 ] {ai_walk(1);}; 136 | void() enf_walk9 =[ $walk9 , enf_walk10 ] {ai_walk(2);}; 137 | void() enf_walk10 =[ $walk10, enf_walk11 ] {ai_walk(4);}; 138 | void() enf_walk11 =[ $walk11, enf_walk12 ] {ai_walk(4);}; 139 | void() enf_walk12 =[ $walk12, enf_walk13 ] {ai_walk(1);}; 140 | void() enf_walk13 =[ $walk13, enf_walk14 ] {ai_walk(2);}; 141 | void() enf_walk14 =[ $walk14, enf_walk15 ] {ai_walk(3);}; 142 | void() enf_walk15 =[ $walk15, enf_walk16 ] {ai_walk(4);}; 143 | void() enf_walk16 =[ $walk16, enf_walk1 ] {ai_walk(2);}; 144 | 145 | void() enf_run1 =[ $run1 , enf_run2 ] { 146 | if (random() < 0.2) 147 | sound (self, CHAN_VOICE, "enforcer/idle1.wav", 1, ATTN_IDLE); 148 | ai_run(18);}; 149 | void() enf_run2 =[ $run2 , enf_run3 ] {ai_run(14);}; 150 | void() enf_run3 =[ $run3 , enf_run4 ] {ai_run(7);}; 151 | void() enf_run4 =[ $run4 , enf_run5 ] {ai_run(12);}; 152 | void() enf_run5 =[ $run5 , enf_run6 ] {ai_run(14);}; 153 | void() enf_run6 =[ $run6 , enf_run7 ] {ai_run(14);}; 154 | void() enf_run7 =[ $run7 , enf_run8 ] {ai_run(7);}; 155 | void() enf_run8 =[ $run8 , enf_run1 ] {ai_run(11);}; 156 | 157 | void() enf_atk1 =[ $attack1, enf_atk2 ] {ai_face();}; 158 | void() enf_atk2 =[ $attack2, enf_atk3 ] {ai_face();}; 159 | void() enf_atk3 =[ $attack3, enf_atk4 ] {ai_face();}; 160 | void() enf_atk4 =[ $attack4, enf_atk5 ] {ai_face();}; 161 | void() enf_atk5 =[ $attack5, enf_atk6 ] {ai_face();}; 162 | void() enf_atk6 =[ $attack6, enf_atk7 ] {enforcer_fire();}; 163 | void() enf_atk7 =[ $attack7, enf_atk8 ] {ai_face();}; 164 | void() enf_atk8 =[ $attack8, enf_atk9 ] {ai_face();}; 165 | void() enf_atk9 =[ $attack5, enf_atk10 ] {ai_face();}; 166 | void() enf_atk10 =[ $attack6, enf_atk11 ] {enforcer_fire();}; 167 | void() enf_atk11 =[ $attack7, enf_atk12 ] {ai_face();}; 168 | void() enf_atk12 =[ $attack8, enf_atk13 ] {ai_face();}; 169 | void() enf_atk13 =[ $attack9, enf_atk14 ] {ai_face();}; 170 | void() enf_atk14 =[ $attack10, enf_run1 ] {ai_face(); 171 | SUB_CheckRefire (enf_atk1); 172 | }; 173 | 174 | void() enf_paina1 =[ $paina1, enf_paina2 ] {}; 175 | void() enf_paina2 =[ $paina2, enf_paina3 ] {}; 176 | void() enf_paina3 =[ $paina3, enf_paina4 ] {}; 177 | void() enf_paina4 =[ $paina4, enf_run1 ] {}; 178 | 179 | void() enf_painb1 =[ $painb1, enf_painb2 ] {}; 180 | void() enf_painb2 =[ $painb2, enf_painb3 ] {}; 181 | void() enf_painb3 =[ $painb3, enf_painb4 ] {}; 182 | void() enf_painb4 =[ $painb4, enf_painb5 ] {}; 183 | void() enf_painb5 =[ $painb5, enf_run1 ] {}; 184 | 185 | void() enf_painc1 =[ $painc1, enf_painc2 ] {}; 186 | void() enf_painc2 =[ $painc2, enf_painc3 ] {}; 187 | void() enf_painc3 =[ $painc3, enf_painc4 ] {}; 188 | void() enf_painc4 =[ $painc4, enf_painc5 ] {}; 189 | void() enf_painc5 =[ $painc5, enf_painc6 ] {}; 190 | void() enf_painc6 =[ $painc6, enf_painc7 ] {}; 191 | void() enf_painc7 =[ $painc7, enf_painc8 ] {}; 192 | void() enf_painc8 =[ $painc8, enf_run1 ] {}; 193 | 194 | void() enf_paind1 =[ $paind1, enf_paind2 ] {}; 195 | void() enf_paind2 =[ $paind2, enf_paind3 ] {}; 196 | void() enf_paind3 =[ $paind3, enf_paind4 ] {}; 197 | void() enf_paind4 =[ $paind4, enf_paind5 ] {ai_painforward(2);}; 198 | void() enf_paind5 =[ $paind5, enf_paind6 ] {ai_painforward(1);}; 199 | void() enf_paind6 =[ $paind6, enf_paind7 ] {}; 200 | void() enf_paind7 =[ $paind7, enf_paind8 ] {}; 201 | void() enf_paind8 =[ $paind8, enf_paind9 ] {}; 202 | void() enf_paind9 =[ $paind9, enf_paind10 ] {}; 203 | void() enf_paind10 =[ $paind10, enf_paind11 ] {}; 204 | void() enf_paind11 =[ $paind11, enf_paind12 ] {ai_painforward(1);}; 205 | void() enf_paind12 =[ $paind12, enf_paind13 ] {ai_painforward(1);}; 206 | void() enf_paind13 =[ $paind13, enf_paind14 ] {ai_painforward(1);}; 207 | void() enf_paind14 =[ $paind14, enf_paind15 ] {}; 208 | void() enf_paind15 =[ $paind15, enf_paind16 ] {}; 209 | void() enf_paind16 =[ $paind16, enf_paind17 ] {ai_pain(1);}; 210 | void() enf_paind17 =[ $paind17, enf_paind18 ] {ai_pain(1);}; 211 | void() enf_paind18 =[ $paind18, enf_paind19 ] {}; 212 | void() enf_paind19 =[ $paind19, enf_run1 ] {}; 213 | 214 | void(entity attacker, float damage) enf_pain = 215 | { 216 | local float r; 217 | 218 | r = random (); 219 | if (self.pain_finished > time) 220 | return; 221 | 222 | 223 | if (r < 0.5) 224 | sound (self, CHAN_VOICE, "enforcer/pain1.wav", 1, ATTN_NORM); 225 | else 226 | sound (self, CHAN_VOICE, "enforcer/pain2.wav", 1, ATTN_NORM); 227 | 228 | if (r < 0.2) 229 | { 230 | self.pain_finished = time + 1; 231 | enf_paina1 (); 232 | } 233 | else if (r < 0.4) 234 | { 235 | self.pain_finished = time + 1; 236 | enf_painb1 (); 237 | } 238 | else if (r < 0.7) 239 | { 240 | self.pain_finished = time + 1; 241 | enf_painc1 (); 242 | } 243 | else 244 | { 245 | self.pain_finished = time + 2; 246 | enf_paind1 (); 247 | } 248 | }; 249 | 250 | //============================================================================ 251 | 252 | 253 | 254 | 255 | void() enf_die1 =[ $death1, enf_die2 ] {}; 256 | void() enf_die2 =[ $death2, enf_die3 ] {}; 257 | void() enf_die3 =[ $death3, enf_die4 ] 258 | {self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();}; 259 | void() enf_die4 =[ $death4, enf_die5 ] {ai_forward(14);}; 260 | void() enf_die5 =[ $death5, enf_die6 ] {ai_forward(2);}; 261 | void() enf_die6 =[ $death6, enf_die7 ] {}; 262 | void() enf_die7 =[ $death7, enf_die8 ] {}; 263 | void() enf_die8 =[ $death8, enf_die9 ] {}; 264 | void() enf_die9 =[ $death9, enf_die10 ] {ai_forward(3);}; 265 | void() enf_die10 =[ $death10, enf_die11 ] {ai_forward(5);}; 266 | void() enf_die11 =[ $death11, enf_die12 ] {ai_forward(5);}; 267 | void() enf_die12 =[ $death12, enf_die13 ] {ai_forward(5);}; 268 | void() enf_die13 =[ $death13, enf_die14 ] {}; 269 | void() enf_die14 =[ $death14, enf_die14 ] {}; 270 | 271 | void() enf_fdie1 =[ $fdeath1, enf_fdie2 ] { 272 | 273 | }; 274 | void() enf_fdie2 =[ $fdeath2, enf_fdie3 ] {}; 275 | void() enf_fdie3 =[ $fdeath3, enf_fdie4 ] 276 | {self.solid = SOLID_NOT;self.ammo_cells = 5;DropBackpack();}; 277 | void() enf_fdie4 =[ $fdeath4, enf_fdie5 ] {}; 278 | void() enf_fdie5 =[ $fdeath5, enf_fdie6 ] {}; 279 | void() enf_fdie6 =[ $fdeath6, enf_fdie7 ] {}; 280 | void() enf_fdie7 =[ $fdeath7, enf_fdie8 ] {}; 281 | void() enf_fdie8 =[ $fdeath8, enf_fdie9 ] {}; 282 | void() enf_fdie9 =[ $fdeath9, enf_fdie10 ] {}; 283 | void() enf_fdie10 =[ $fdeath10, enf_fdie11 ] {}; 284 | void() enf_fdie11 =[ $fdeath11, enf_fdie11 ] {}; 285 | 286 | 287 | void() enf_die = 288 | { 289 | // check for gib 290 | if (self.health < -35) 291 | { 292 | ThrowHead ("progs/h_mega.mdl", self.health); 293 | ThrowGib ("progs/gib1.mdl", self.health); 294 | ThrowGib ("progs/gib2.mdl", self.health); 295 | ThrowGib ("progs/gib3.mdl", self.health); 296 | // spinstagib sounds 297 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 298 | { 299 | GibSoundsRandom(); 300 | return; 301 | } 302 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 303 | return; 304 | } 305 | 306 | // regular death 307 | sound (self, CHAN_VOICE, "enforcer/death1.wav", 1, ATTN_NORM); 308 | if (random() > 0.5) 309 | enf_die1 (); 310 | else 311 | enf_fdie1 (); 312 | }; 313 | 314 | 315 | /*QUAKED monster_enforcer (1 0 0) (-16 -16 -24) (16 16 40) Ambush 316 | 317 | */ 318 | void() monster_enforcer = 319 | { 320 | if (deathmatch) 321 | { 322 | remove(self); 323 | return; 324 | } 325 | precache_model2 ("progs/enforcer.mdl"); 326 | precache_model2 ("progs/h_mega.mdl"); 327 | precache_model2 ("progs/laser.mdl"); 328 | 329 | precache_sound2 ("enforcer/death1.wav"); 330 | precache_sound2 ("enforcer/enfire.wav"); 331 | precache_sound2 ("enforcer/enfstop.wav"); 332 | precache_sound2 ("enforcer/idle1.wav"); 333 | precache_sound2 ("enforcer/pain1.wav"); 334 | precache_sound2 ("enforcer/pain2.wav"); 335 | precache_sound2 ("enforcer/sight1.wav"); 336 | precache_sound2 ("enforcer/sight2.wav"); 337 | precache_sound2 ("enforcer/sight3.wav"); 338 | precache_sound2 ("enforcer/sight4.wav"); 339 | 340 | self.solid = SOLID_SLIDEBOX; 341 | self.movetype = MOVETYPE_STEP; 342 | 343 | setmodel (self, "progs/enforcer.mdl"); 344 | 345 | setsize (self, '-16 -16 -24', '16 16 40'); 346 | self.health = 80; 347 | 348 | self.th_stand = enf_stand1; 349 | self.th_walk = enf_walk1; 350 | self.th_run = enf_run1; 351 | self.th_pain = enf_pain; 352 | self.th_die = enf_die; 353 | self.th_missile = enf_atk1; 354 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 355 | self.touch = monster_touch; 356 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 357 | 358 | walkmonster_start(); 359 | }; 360 | -------------------------------------------------------------------------------- /src/fight.qc: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | A monster is in fight mode if it thinks it can effectively attack its 5 | enemy. 6 | 7 | When it decides it can't attack, it goes into hunt mode. 8 | 9 | */ 10 | 11 | float(float v) anglemod; 12 | 13 | void() knight_atk1; 14 | void() knight_runatk1; 15 | void() ogre_smash1; 16 | void() ogre_swing1; 17 | 18 | void() sham_smash1; 19 | void() sham_swingr1; 20 | void() sham_swingl1; 21 | 22 | float() DemonCheckAttack; 23 | void(float side) Demon_Melee; 24 | 25 | void(vector dest) ChooseTurn; 26 | 27 | void() ai_face; 28 | 29 | 30 | float enemy_vis, enemy_infront, enemy_range; 31 | float enemy_yaw; 32 | 33 | 34 | void() knight_attack = 35 | { 36 | local float len; 37 | 38 | // decide if now is a good swing time 39 | len = vlen(self.enemy.origin+self.enemy.view_ofs - (self.origin+self.view_ofs)); 40 | 41 | if (len<80) 42 | knight_atk1 (); 43 | else 44 | knight_runatk1 (); 45 | }; 46 | 47 | //============================================================================= 48 | 49 | /* 50 | =========== 51 | CheckAttack 52 | 53 | The player is in view, so decide to move or launch an attack 54 | Returns FALSE if movement should continue 55 | ============ 56 | */ 57 | float() CheckAttack = 58 | { 59 | local vector spot1, spot2; 60 | local entity targ; 61 | local float chance; 62 | 63 | targ = self.enemy; 64 | 65 | // see if any entities are in the way of the shot 66 | spot1 = self.origin + self.view_ofs; 67 | spot2 = targ.origin + targ.view_ofs; 68 | 69 | traceline (spot1, spot2, FALSE, self); 70 | 71 | if (trace_ent != targ) 72 | return FALSE; // don't have a clear shot 73 | 74 | if (trace_inopen && trace_inwater) 75 | return FALSE; // sight line crossed contents 76 | 77 | if (enemy_range == RANGE_MELEE) 78 | { // melee attack 79 | if (self.th_melee) 80 | { 81 | if (self.classname == "monster_knight") 82 | knight_attack (); 83 | else 84 | self.th_melee (); 85 | return TRUE; 86 | } 87 | } 88 | 89 | // missile attack 90 | if (!self.th_missile) 91 | return FALSE; 92 | 93 | if (time < self.attack_finished) 94 | return FALSE; 95 | 96 | if (enemy_range == RANGE_FAR) 97 | return FALSE; 98 | 99 | if (enemy_range == RANGE_MELEE) 100 | { 101 | chance = 0.9; 102 | self.attack_finished = 0; 103 | } 104 | else if (enemy_range == RANGE_NEAR) 105 | { 106 | if (self.th_melee) 107 | chance = 0.2; 108 | else 109 | chance = 0.4; 110 | } 111 | else if (enemy_range == RANGE_MID) 112 | { 113 | if (self.th_melee) 114 | chance = 0.05; 115 | else 116 | chance = 0.1; 117 | } 118 | else 119 | chance = 0; 120 | 121 | if (random () < chance) 122 | { 123 | self.th_missile (); 124 | SUB_AttackFinished (2*random()); 125 | return TRUE; 126 | } 127 | 128 | return FALSE; 129 | }; 130 | 131 | 132 | /* 133 | ============= 134 | ai_face 135 | 136 | Stay facing the enemy 137 | ============= 138 | */ 139 | void() ai_face = 140 | { 141 | self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); 142 | ChangeYaw (); 143 | }; 144 | 145 | /* 146 | ============= 147 | ai_charge 148 | 149 | The monster is in a melee attack, so get as close as possible to .enemy 150 | ============= 151 | */ 152 | float (entity targ) visible; 153 | float(entity targ) infront; 154 | float(entity targ) range; 155 | 156 | void(float d) ai_charge = 157 | { 158 | ai_face (); 159 | movetogoal (d); // done in C code... 160 | }; 161 | 162 | void() ai_charge_side = 163 | { 164 | local vector dtemp; 165 | local float heading; 166 | 167 | // aim to the left of the enemy for a flyby 168 | 169 | self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); 170 | ChangeYaw (); 171 | 172 | makevectors (self.angles); 173 | dtemp = self.enemy.origin - 30*v_right; 174 | heading = vectoyaw(dtemp - self.origin); 175 | 176 | walkmove(heading, 20); 177 | }; 178 | 179 | 180 | /* 181 | ============= 182 | ai_melee 183 | 184 | ============= 185 | */ 186 | void() ai_melee = 187 | { 188 | local vector delta; 189 | local float ldmg; 190 | 191 | if (!self.enemy) 192 | return; // removed before stroke 193 | 194 | delta = self.enemy.origin - self.origin; 195 | 196 | if (vlen(delta) > 60) 197 | return; 198 | 199 | ldmg = (random() + random() + random()) * 3; 200 | T_Damage (self.enemy, self, self, ldmg); 201 | }; 202 | 203 | 204 | void() ai_melee_side = 205 | { 206 | local vector delta; 207 | local float ldmg; 208 | 209 | if (!self.enemy) 210 | return; // removed before stroke 211 | 212 | ai_charge_side(); 213 | 214 | delta = self.enemy.origin - self.origin; 215 | 216 | if (vlen(delta) > 60) 217 | return; 218 | if (!CanDamage (self.enemy, self)) 219 | return; 220 | ldmg = (random() + random() + random()) * 3; 221 | T_Damage (self.enemy, self, self, ldmg); 222 | }; 223 | 224 | 225 | //============================================================================= 226 | 227 | /* 228 | =========== 229 | SoldierCheckAttack 230 | 231 | The player is in view, so decide to move or launch an attack 232 | Returns FALSE if movement should continue 233 | ============ 234 | */ 235 | float() SoldierCheckAttack = 236 | { 237 | local vector spot1, spot2; 238 | local entity targ; 239 | local float chance; 240 | 241 | targ = self.enemy; 242 | 243 | // see if any entities are in the way of the shot 244 | spot1 = self.origin + self.view_ofs; 245 | spot2 = targ.origin + targ.view_ofs; 246 | 247 | traceline (spot1, spot2, FALSE, self); 248 | 249 | if (trace_inopen && trace_inwater) 250 | return FALSE; // sight line crossed contents 251 | 252 | if (trace_ent != targ) 253 | return FALSE; // don't have a clear shot 254 | 255 | 256 | // missile attack 257 | if (time < self.attack_finished) 258 | return FALSE; 259 | 260 | if (enemy_range == RANGE_FAR) 261 | return FALSE; 262 | 263 | if (enemy_range == RANGE_MELEE) 264 | chance = 0.9; 265 | else if (enemy_range == RANGE_NEAR) 266 | chance = 0.4; 267 | else if (enemy_range == RANGE_MID) 268 | chance = 0.05; 269 | else 270 | chance = 0; 271 | 272 | if (random () < chance) 273 | { 274 | self.th_missile (); 275 | SUB_AttackFinished (1 + random()); 276 | if (random() < 0.3) 277 | self.lefty = !self.lefty; 278 | 279 | return TRUE; 280 | } 281 | 282 | return FALSE; 283 | }; 284 | //============================================================================= 285 | 286 | /* 287 | =========== 288 | ShamCheckAttack 289 | 290 | The player is in view, so decide to move or launch an attack 291 | Returns FALSE if movement should continue 292 | ============ 293 | */ 294 | float() ShamCheckAttack = 295 | { 296 | local vector spot1, spot2; 297 | local entity targ; 298 | 299 | if (enemy_range == RANGE_MELEE) 300 | { 301 | if (CanDamage (self.enemy, self)) 302 | { 303 | self.attack_state = AS_MELEE; 304 | return TRUE; 305 | } 306 | } 307 | 308 | if (time < self.attack_finished) 309 | return FALSE; 310 | 311 | if (!enemy_vis) 312 | return FALSE; 313 | 314 | targ = self.enemy; 315 | 316 | // see if any entities are in the way of the shot 317 | spot1 = self.origin + self.view_ofs; 318 | spot2 = targ.origin + targ.view_ofs; 319 | 320 | if (vlen(spot1 - spot2) > 600) 321 | return FALSE; 322 | 323 | traceline (spot1, spot2, FALSE, self); 324 | 325 | if (trace_inopen && trace_inwater) 326 | return FALSE; // sight line crossed contents 327 | 328 | if (trace_ent != targ) 329 | { 330 | return FALSE; // don't have a clear shot 331 | } 332 | 333 | // missile attack 334 | if (enemy_range == RANGE_FAR) 335 | return FALSE; 336 | 337 | self.attack_state = AS_MISSILE; 338 | SUB_AttackFinished (2 + 2*random()); 339 | return TRUE; 340 | }; 341 | 342 | //============================================================================ 343 | 344 | /* 345 | =========== 346 | OgreCheckAttack 347 | 348 | The player is in view, so decide to move or launch an attack 349 | Returns FALSE if movement should continue 350 | ============ 351 | */ 352 | float() OgreCheckAttack = 353 | { 354 | local vector spot1, spot2; 355 | local entity targ; 356 | local float chance; 357 | 358 | if (enemy_range == RANGE_MELEE) 359 | { 360 | if (CanDamage (self.enemy, self)) 361 | { 362 | self.attack_state = AS_MELEE; 363 | return TRUE; 364 | } 365 | } 366 | 367 | if (time < self.attack_finished) 368 | return FALSE; 369 | 370 | if (!enemy_vis) 371 | return FALSE; 372 | 373 | targ = self.enemy; 374 | 375 | // see if any entities are in the way of the shot 376 | spot1 = self.origin + self.view_ofs; 377 | spot2 = targ.origin + targ.view_ofs; 378 | 379 | traceline (spot1, spot2, FALSE, self); 380 | 381 | if (trace_inopen && trace_inwater) 382 | return FALSE; // sight line crossed contents 383 | 384 | if (trace_ent != targ) 385 | { 386 | return FALSE; // don't have a clear shot 387 | } 388 | 389 | // missile attack 390 | if (time < self.attack_finished) 391 | return FALSE; 392 | 393 | if (enemy_range == RANGE_FAR) 394 | return FALSE; 395 | 396 | else if (enemy_range == RANGE_NEAR) 397 | chance = 0.10; 398 | else if (enemy_range == RANGE_MID) 399 | chance = 0.05; 400 | else 401 | chance = 0; 402 | 403 | self.attack_state = AS_MISSILE; 404 | SUB_AttackFinished (1 + 2*random()); 405 | return TRUE; 406 | }; 407 | 408 | -------------------------------------------------------------------------------- /src/fish.qc: -------------------------------------------------------------------------------- 1 | $cd id1/models/fish 2 | $origin 0 0 24 3 | $base base 4 | $skin skin 5 | 6 | $frame attack1 attack2 attack3 attack4 attack5 attack6 7 | $frame attack7 attack8 attack9 attack10 attack11 attack12 attack13 8 | $frame attack14 attack15 attack16 attack17 attack18 9 | 10 | $frame death1 death2 death3 death4 death5 death6 death7 11 | $frame death8 death9 death10 death11 death12 death13 death14 death15 12 | $frame death16 death17 death18 death19 death20 death21 13 | 14 | $frame swim1 swim2 swim3 swim4 swim5 swim6 swim7 swim8 15 | $frame swim9 swim10 swim11 swim12 swim13 swim14 swim15 swim16 swim17 16 | $frame swim18 17 | 18 | $frame pain1 pain2 pain3 pain4 pain5 pain6 pain7 pain8 19 | $frame pain9 20 | 21 | void() swimmonster_start; 22 | 23 | void() f_stand1 =[ $swim1, f_stand2 ] {ai_stand();}; 24 | void() f_stand2 =[ $swim2, f_stand3 ] {ai_stand();}; 25 | void() f_stand3 =[ $swim3, f_stand4 ] {ai_stand();}; 26 | void() f_stand4 =[ $swim4, f_stand5 ] {ai_stand();}; 27 | void() f_stand5 =[ $swim5, f_stand6 ] {ai_stand();}; 28 | void() f_stand6 =[ $swim6, f_stand7 ] {ai_stand();}; 29 | void() f_stand7 =[ $swim7, f_stand8 ] {ai_stand();}; 30 | void() f_stand8 =[ $swim8, f_stand9 ] {ai_stand();}; 31 | void() f_stand9 =[ $swim9, f_stand10 ] {ai_stand();}; 32 | void() f_stand10 =[ $swim10, f_stand11 ] {ai_stand();}; 33 | void() f_stand11 =[ $swim11, f_stand12 ] {ai_stand();}; 34 | void() f_stand12 =[ $swim12, f_stand13 ] {ai_stand();}; 35 | void() f_stand13 =[ $swim13, f_stand14 ] {ai_stand();}; 36 | void() f_stand14 =[ $swim14, f_stand15 ] {ai_stand();}; 37 | void() f_stand15 =[ $swim15, f_stand16 ] {ai_stand();}; 38 | void() f_stand16 =[ $swim16, f_stand17 ] {ai_stand();}; 39 | void() f_stand17 =[ $swim17, f_stand18 ] {ai_stand();}; 40 | void() f_stand18 =[ $swim18, f_stand1 ] {ai_stand();}; 41 | 42 | void() f_walk1 =[ $swim1, f_walk2 ] {ai_walk(8);}; 43 | void() f_walk2 =[ $swim2, f_walk3 ] {ai_walk(8);}; 44 | void() f_walk3 =[ $swim3, f_walk4 ] {ai_walk(8);}; 45 | void() f_walk4 =[ $swim4, f_walk5 ] {ai_walk(8);}; 46 | void() f_walk5 =[ $swim5, f_walk6 ] {ai_walk(8);}; 47 | void() f_walk6 =[ $swim6, f_walk7 ] {ai_walk(8);}; 48 | void() f_walk7 =[ $swim7, f_walk8 ] {ai_walk(8);}; 49 | void() f_walk8 =[ $swim8, f_walk9 ] {ai_walk(8);}; 50 | void() f_walk9 =[ $swim9, f_walk10 ] {ai_walk(8);}; 51 | void() f_walk10 =[ $swim10, f_walk11 ] {ai_walk(8);}; 52 | void() f_walk11 =[ $swim11, f_walk12 ] {ai_walk(8);}; 53 | void() f_walk12 =[ $swim12, f_walk13 ] {ai_walk(8);}; 54 | void() f_walk13 =[ $swim13, f_walk14 ] {ai_walk(8);}; 55 | void() f_walk14 =[ $swim14, f_walk15 ] {ai_walk(8);}; 56 | void() f_walk15 =[ $swim15, f_walk16 ] {ai_walk(8);}; 57 | void() f_walk16 =[ $swim16, f_walk17 ] {ai_walk(8);}; 58 | void() f_walk17 =[ $swim17, f_walk18 ] {ai_walk(8);}; 59 | void() f_walk18 =[ $swim18, f_walk1 ] {ai_walk(8);}; 60 | 61 | void() f_run1 =[ $swim1, f_run2 ] {ai_run(12); 62 | if (random() < 0.5) 63 | sound (self, CHAN_VOICE, "fish/idle.wav", 1, ATTN_NORM); 64 | }; 65 | void() f_run2 =[ $swim3, f_run3 ] {ai_run(12);}; 66 | void() f_run3 =[ $swim5, f_run4 ] {ai_run(12);}; 67 | void() f_run4 =[ $swim7, f_run5 ] {ai_run(12);}; 68 | void() f_run5 =[ $swim9, f_run6 ] {ai_run(12);}; 69 | void() f_run6 =[ $swim11, f_run7 ] {ai_run(12);}; 70 | void() f_run7 =[ $swim13, f_run8 ] {ai_run(12);}; 71 | void() f_run8 =[ $swim15, f_run9 ] {ai_run(12);}; 72 | void() f_run9 =[ $swim17, f_run1 ] {ai_run(12);}; 73 | 74 | void() fish_melee = 75 | { 76 | local vector delta; 77 | local float ldmg; 78 | 79 | if (!self.enemy) 80 | return; // removed before stroke 81 | 82 | delta = self.enemy.origin - self.origin; 83 | 84 | if (vlen(delta) > 60) 85 | return; 86 | 87 | sound (self, CHAN_VOICE, "fish/bite.wav", 1, ATTN_NORM); 88 | ldmg = (random() + random()) * 3; 89 | T_Damage (self.enemy, self, self, ldmg); 90 | }; 91 | 92 | void() f_attack1 =[ $attack1, f_attack2 ] {ai_charge(10);}; 93 | void() f_attack2 =[ $attack2, f_attack3 ] {ai_charge(10);}; 94 | void() f_attack3 =[ $attack3, f_attack4 ] {fish_melee();}; 95 | void() f_attack4 =[ $attack4, f_attack5 ] {ai_charge(10);}; 96 | void() f_attack5 =[ $attack5, f_attack6 ] {ai_charge(10);}; 97 | void() f_attack6 =[ $attack6, f_attack7 ] {ai_charge(10);}; 98 | void() f_attack7 =[ $attack7, f_attack8 ] {ai_charge(10);}; 99 | void() f_attack8 =[ $attack8, f_attack9 ] {ai_charge(10);}; 100 | void() f_attack9 =[ $attack9, f_attack10] {fish_melee();}; 101 | void() f_attack10 =[ $attack10, f_attack11] {ai_charge(10);}; 102 | void() f_attack11 =[ $attack11, f_attack12] {ai_charge(10);}; 103 | void() f_attack12 =[ $attack12, f_attack13] {ai_charge(10);}; 104 | void() f_attack13 =[ $attack13, f_attack14] {ai_charge(10);}; 105 | void() f_attack14 =[ $attack14, f_attack15] {ai_charge(10);}; 106 | void() f_attack15 =[ $attack15, f_attack16] {fish_melee();}; 107 | void() f_attack16 =[ $attack16, f_attack17] {ai_charge(10);}; 108 | void() f_attack17 =[ $attack17, f_attack18] {ai_charge(10);}; 109 | void() f_attack18 =[ $attack18, f_run1 ] {ai_charge(10);}; 110 | 111 | void() f_death1 =[ $death1, f_death2 ] { 112 | sound (self, CHAN_VOICE, "fish/death.wav", 1, ATTN_NORM); 113 | }; 114 | void() f_death2 =[ $death2, f_death3 ] {}; 115 | void() f_death3 =[ $death3, f_death4 ] {}; 116 | void() f_death4 =[ $death4, f_death5 ] {self.solid = SOLID_NOT;}; // fish collision fix -- dumptruck_ds 2021/4/8 117 | void() f_death5 =[ $death5, f_death6 ] {}; 118 | void() f_death6 =[ $death6, f_death7 ] {}; 119 | void() f_death7 =[ $death7, f_death8 ] {}; 120 | void() f_death8 =[ $death8, f_death9 ] {}; 121 | void() f_death9 =[ $death9, f_death10 ] {}; 122 | void() f_death10 =[ $death10, f_death11 ] {}; 123 | void() f_death11 =[ $death11, f_death12 ] {}; 124 | void() f_death12 =[ $death12, f_death13 ] {}; 125 | void() f_death13 =[ $death13, f_death14 ] {}; 126 | void() f_death14 =[ $death14, f_death15 ] {}; 127 | void() f_death15 =[ $death15, f_death16 ] {}; 128 | void() f_death16 =[ $death16, f_death17 ] {}; 129 | void() f_death17 =[ $death17, f_death18 ] {}; 130 | void() f_death18 =[ $death18, f_death19 ] {}; 131 | void() f_death19 =[ $death19, f_death20 ] {}; 132 | void() f_death20 =[ $death20, f_death21 ] {}; 133 | void() f_death21 =[ $death21, f_death21 ] {/*self.solid = SOLID_NOT;*/}; // fish collision fix -- dumptruck_ds 2021/4/8 134 | 135 | void() f_pain1 =[ $pain1, f_pain2 ] {}; 136 | void() f_pain2 =[ $pain2, f_pain3 ] {ai_pain(6);}; 137 | void() f_pain3 =[ $pain3, f_pain4 ] {ai_pain(6);}; 138 | void() f_pain4 =[ $pain4, f_pain5 ] {ai_pain(6);}; 139 | void() f_pain5 =[ $pain5, f_pain6 ] {ai_pain(6);}; 140 | void() f_pain6 =[ $pain6, f_pain7 ] {ai_pain(6);}; 141 | void() f_pain7 =[ $pain7, f_pain8 ] {ai_pain(6);}; 142 | void() f_pain8 =[ $pain8, f_pain9 ] {ai_pain(6);}; 143 | void() f_pain9 =[ $pain9, f_run1 ] {ai_pain(6);}; 144 | 145 | void(entity attacker, float damage) fish_pain = 146 | { 147 | 148 | // fish allways do pain frames 149 | f_pain1 (); 150 | }; 151 | // 1998-08-09 Gibbable fishes by Maddes/Athos start 152 | void() f_die = 153 | { 154 | // check for gib 155 | if (self.health < -35) 156 | { 157 | ThrowHead ("progs/gib3.mdl", self.health); 158 | ThrowGib ("progs/gib3.mdl", self.health); 159 | // spinstagib sounds 160 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 161 | { 162 | GibSoundsRandom(); 163 | return; 164 | } 165 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 166 | return; 167 | } 168 | // regular death 169 | f_death1 (); 170 | }; 171 | // 1998-08-09 Gibbable fishes by Maddes/Athos end 172 | 173 | /*QUAKED monster_fish (1 0 0) (-16 -16 -24) (16 16 24) Ambush 174 | */ 175 | void() monster_fish = 176 | { 177 | if (deathmatch) 178 | { 179 | remove(self); 180 | return; 181 | } 182 | precache_model2 ("progs/fish.mdl"); 183 | 184 | precache_sound2 ("fish/death.wav"); 185 | precache_sound2 ("fish/bite.wav"); 186 | precache_sound2 ("fish/idle.wav"); 187 | 188 | self.solid = SOLID_SLIDEBOX; 189 | self.movetype = MOVETYPE_STEP; 190 | 191 | setmodel (self, "progs/fish.mdl"); 192 | 193 | setsize (self, '-16 -16 -24', '16 16 24'); 194 | self.health = 25; 195 | 196 | self.th_stand = f_stand1; 197 | self.th_walk = f_walk1; 198 | self.th_run = f_run1; 199 | // 1998-08-09 Gibbable fishes by Maddes/Athos start 200 | // self.th_die = f_death1; 201 | self.th_die = f_die; 202 | // 1998-08-09 Gibbable fishes by Maddes/Athos end self.th_pain = fish_pain; 203 | self.th_melee = f_attack1; 204 | 205 | swimmonster_start (); 206 | }; 207 | -------------------------------------------------------------------------------- /src/flag.qc: -------------------------------------------------------------------------------- 1 | /*QUAKED item_deathball (.3 .3 1) (0 0 0) (32 32 32) 2 | */ 3 | void() deathball_touch; 4 | 5 | void() item_deathball = 6 | { 7 | self.touch = deathball_touch; 8 | }; -------------------------------------------------------------------------------- /src/jctest.qc: -------------------------------------------------------------------------------- 1 | 2 | void() jctrig = 3 | { 4 | dprint ("here\n\n"); 5 | lightstyle(0, "az"); 6 | }; 7 | 8 | /*QUAKED trigger_jctest (.5 .5 .5) ? 9 | */ 10 | void() trigger_jctest = 11 | { 12 | setsize (self, self.mins, self.maxs); 13 | self.solid = SOLID_EDGE; 14 | self.touch = jctrig; 15 | }; 16 | -------------------------------------------------------------------------------- /src/knight.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | KNIGHT 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/knight 10 | $origin 0 0 24 11 | $base base 12 | $skin badass3 13 | 14 | $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9 15 | 16 | $frame runb1 runb2 runb3 runb4 runb5 runb6 runb7 runb8 17 | 18 | //frame runc1 runc2 runc3 runc4 runc5 runc6 19 | 20 | $frame runattack1 runattack2 runattack3 runattack4 runattack5 21 | $frame runattack6 runattack7 runattack8 runattack9 runattack10 22 | $frame runattack11 23 | 24 | $frame pain1 pain2 pain3 25 | 26 | $frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 27 | $frame painb10 painb11 28 | 29 | //frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 30 | //frame attack8 attack9 attack10 attack11 31 | 32 | $frame attackdummy 33 | $frame attackb1 attackb2 attackb3 attackb4 attackb5 34 | $frame attackb6 attackb7 attackb8 attackb9 attackb10 35 | 36 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 37 | $frame walk10 walk11 walk12 walk13 walk14 38 | 39 | $frame kneel1 kneel2 kneel3 kneel4 kneel5 40 | 41 | $frame standing2 standing3 standing4 standing5 42 | 43 | $frame death1 death2 death3 death4 death5 death6 death7 death8 44 | $frame death9 death10 45 | 46 | $frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8 47 | $frame deathb9 deathb10 deathb11 48 | 49 | void() knight_stand1 =[ $stand1, knight_stand2 ] {ai_stand();}; 50 | void() knight_stand2 =[ $stand2, knight_stand3 ] {ai_stand();}; 51 | void() knight_stand3 =[ $stand3, knight_stand4 ] {ai_stand();}; 52 | void() knight_stand4 =[ $stand4, knight_stand5 ] {ai_stand();}; 53 | void() knight_stand5 =[ $stand5, knight_stand6 ] {ai_stand();}; 54 | void() knight_stand6 =[ $stand6, knight_stand7 ] {ai_stand();}; 55 | void() knight_stand7 =[ $stand7, knight_stand8 ] {ai_stand();}; 56 | void() knight_stand8 =[ $stand8, knight_stand9 ] {ai_stand();}; 57 | void() knight_stand9 =[ $stand9, knight_stand1 ] {ai_stand();}; 58 | 59 | void() knight_walk1 =[ $walk1, knight_walk2 ] { 60 | if (random() < 0.2) 61 | sound (self, CHAN_VOICE, "knight/idle.wav", 1, ATTN_IDLE); 62 | ai_walk(3);}; 63 | void() knight_walk2 =[ $walk2, knight_walk3 ] {ai_walk(2);}; 64 | void() knight_walk3 =[ $walk3, knight_walk4 ] {ai_walk(3);}; 65 | void() knight_walk4 =[ $walk4, knight_walk5 ] {ai_walk(4);}; 66 | void() knight_walk5 =[ $walk5, knight_walk6 ] {ai_walk(3);}; 67 | void() knight_walk6 =[ $walk6, knight_walk7 ] {ai_walk(3);}; 68 | void() knight_walk7 =[ $walk7, knight_walk8 ] {ai_walk(3);}; 69 | void() knight_walk8 =[ $walk8, knight_walk9 ] {ai_walk(4);}; 70 | void() knight_walk9 =[ $walk9, knight_walk10 ] {ai_walk(3);}; 71 | void() knight_walk10 =[ $walk10, knight_walk11 ] {ai_walk(3);}; 72 | void() knight_walk11 =[ $walk11, knight_walk12 ] {ai_walk(2);}; 73 | void() knight_walk12 =[ $walk12, knight_walk13 ] {ai_walk(3);}; 74 | void() knight_walk13 =[ $walk13, knight_walk14 ] {ai_walk(4);}; 75 | void() knight_walk14 =[ $walk14, knight_walk1 ] {ai_walk(3);}; 76 | 77 | 78 | void() knight_run1 =[ $runb1, knight_run2 ] { 79 | if (random() < 0.2) 80 | sound (self, CHAN_VOICE, "knight/idle.wav", 1, ATTN_IDLE); 81 | ai_run(16);}; 82 | void() knight_run2 =[ $runb2, knight_run3 ] {ai_run(20);}; 83 | void() knight_run3 =[ $runb3, knight_run4 ] {ai_run(13);}; 84 | void() knight_run4 =[ $runb4, knight_run5 ] {ai_run(7);}; 85 | void() knight_run5 =[ $runb5, knight_run6 ] {ai_run(16);}; 86 | void() knight_run6 =[ $runb6, knight_run7 ] {ai_run(20);}; 87 | void() knight_run7 =[ $runb7, knight_run8 ] {ai_run(14);}; 88 | void() knight_run8 =[ $runb8, knight_run1 ] {ai_run(6);}; 89 | 90 | 91 | void() knight_runatk1 =[ $runattack1, knight_runatk2 ] 92 | { 93 | if (random() > 0.5) 94 | sound (self, CHAN_WEAPON, "knight/sword2.wav", 1, ATTN_NORM); 95 | else 96 | sound (self, CHAN_WEAPON, "knight/sword1.wav", 1, ATTN_NORM); 97 | ai_charge(20); 98 | }; 99 | void() knight_runatk2 =[ $runattack2, knight_runatk3 ] {ai_charge_side();}; 100 | void() knight_runatk3 =[ $runattack3, knight_runatk4 ] {ai_charge_side();}; 101 | void() knight_runatk4 =[ $runattack4, knight_runatk5 ] {ai_charge_side();}; 102 | void() knight_runatk5 =[ $runattack5, knight_runatk6 ] {ai_melee_side();}; 103 | void() knight_runatk6 =[ $runattack6, knight_runatk7 ] {ai_melee_side();}; 104 | void() knight_runatk7 =[ $runattack7, knight_runatk8 ] {ai_melee_side();}; 105 | void() knight_runatk8 =[ $runattack8, knight_runatk9 ] {ai_melee_side();}; 106 | void() knight_runatk9 =[ $runattack9, knight_runatk10 ] {ai_melee_side();}; 107 | void() knight_runatk10 =[ $runattack10, knight_runatk11 ] {ai_charge_side();}; 108 | void() knight_runatk11 =[ $runattack11, knight_run1 ] {ai_charge(10);}; 109 | 110 | void() knight_atk1 =[ $attackb1, knight_atk2 ] 111 | { 112 | sound (self, CHAN_WEAPON, "knight/sword1.wav", 1, ATTN_NORM); 113 | ai_charge(0);}; 114 | void() knight_atk2 =[ $attackb2, knight_atk3 ] {ai_charge(7);}; 115 | void() knight_atk3 =[ $attackb3, knight_atk4 ] {ai_charge(4);}; 116 | void() knight_atk4 =[ $attackb4, knight_atk5 ] {ai_charge(0);}; 117 | void() knight_atk5 =[ $attackb5, knight_atk6 ] {ai_charge(3);}; 118 | void() knight_atk6 =[ $attackb6, knight_atk7 ] {ai_charge(4); ai_melee();}; 119 | void() knight_atk7 =[ $attackb7, knight_atk8 ] {ai_charge(1); ai_melee();}; 120 | void() knight_atk8 =[ $attackb8, knight_atk9 ] {ai_charge(3); 121 | ai_melee();}; 122 | void() knight_atk9 =[ $attackb9, knight_atk10] {ai_charge(1);}; 123 | void() knight_atk10=[ $attackb10, knight_run1 ] {ai_charge(5);}; 124 | 125 | //void() knight_atk9 =[ $attack9, knight_atk10 ] {}; 126 | //void() knight_atk10 =[ $attack10, knight_atk11 ] {}; 127 | //void() knight_atk11 =[ $attack11, knight_run1 ] {}; 128 | 129 | //=========================================================================== 130 | 131 | void() knight_pain1 =[ $pain1, knight_pain2 ] {}; 132 | void() knight_pain2 =[ $pain2, knight_pain3 ] {}; 133 | void() knight_pain3 =[ $pain3, knight_run1 ] {}; 134 | 135 | void() knight_painb1 =[ $painb1, knight_painb2 ] {ai_painforward(0);}; 136 | void() knight_painb2 =[ $painb2, knight_painb3 ] {ai_painforward(3);}; 137 | void() knight_painb3 =[ $painb3, knight_painb4 ] {}; 138 | void() knight_painb4 =[ $painb4, knight_painb5 ] {}; 139 | void() knight_painb5 =[ $painb5, knight_painb6 ] {ai_painforward(2);}; 140 | void() knight_painb6 =[ $painb6, knight_painb7 ] {ai_painforward(4);}; 141 | void() knight_painb7 =[ $painb7, knight_painb8 ] {ai_painforward(2);}; 142 | void() knight_painb8 =[ $painb8, knight_painb9 ] {ai_painforward(5);}; 143 | void() knight_painb9 =[ $painb9, knight_painb10 ] {ai_painforward(5);}; 144 | void() knight_painb10 =[ $painb10, knight_painb11 ] {ai_painforward(0);}; 145 | void() knight_painb11 =[ $painb11, knight_run1 ] {}; 146 | 147 | void(entity attacker, float damage) knight_pain = 148 | { 149 | local float r; 150 | 151 | if (self.pain_finished > time) 152 | return; 153 | 154 | r = random(); 155 | 156 | sound (self, CHAN_VOICE, "knight/khurt.wav", 1, ATTN_NORM); 157 | if (r < 0.85) 158 | { 159 | knight_pain1 (); 160 | self.pain_finished = time + 1; 161 | } 162 | else 163 | { 164 | knight_painb1 (); 165 | self.pain_finished = time + 1; 166 | } 167 | 168 | }; 169 | 170 | //=========================================================================== 171 | 172 | void() knight_bow1 =[ $kneel1, knight_bow2 ] {ai_turn();}; 173 | void() knight_bow2 =[ $kneel2, knight_bow3 ] {ai_turn();}; 174 | void() knight_bow3 =[ $kneel3, knight_bow4 ] {ai_turn();}; 175 | void() knight_bow4 =[ $kneel4, knight_bow5 ] {ai_turn();}; 176 | 177 | void() knight_bow5 =[ $kneel5, knight_bow5 ] {ai_turn();}; 178 | 179 | void() knight_bow6 =[ $kneel4, knight_bow7 ] {ai_turn();}; 180 | void() knight_bow7 =[ $kneel3, knight_bow8 ] {ai_turn();}; 181 | void() knight_bow8 =[ $kneel2, knight_bow9 ] {ai_turn();}; 182 | void() knight_bow9 =[ $kneel1, knight_bow10 ] {ai_turn();}; 183 | void() knight_bow10 =[ $walk1, knight_walk1 ] {ai_turn();}; 184 | 185 | 186 | 187 | void() knight_die1 =[ $death1, knight_die2 ] {}; 188 | void() knight_die2 =[ $death2, knight_die3 ] {}; 189 | void() knight_die3 =[ $death3, knight_die4 ] 190 | {self.solid = SOLID_NOT;}; 191 | void() knight_die4 =[ $death4, knight_die5 ] {}; 192 | void() knight_die5 =[ $death5, knight_die6 ] {}; 193 | void() knight_die6 =[ $death6, knight_die7 ] {}; 194 | void() knight_die7 =[ $death7, knight_die8 ] {}; 195 | void() knight_die8 =[ $death8, knight_die9 ] {}; 196 | void() knight_die9 =[ $death9, knight_die10] {}; 197 | void() knight_die10=[ $death10, knight_die10] {}; 198 | 199 | 200 | void() knight_dieb1 =[ $deathb1, knight_dieb2 ] {}; 201 | void() knight_dieb2 =[ $deathb2, knight_dieb3 ] {}; 202 | void() knight_dieb3 =[ $deathb3, knight_dieb4 ] 203 | {self.solid = SOLID_NOT;}; 204 | void() knight_dieb4 =[ $deathb4, knight_dieb5 ] {}; 205 | void() knight_dieb5 =[ $deathb5, knight_dieb6 ] {}; 206 | void() knight_dieb6 =[ $deathb6, knight_dieb7 ] {}; 207 | void() knight_dieb7 =[ $deathb7, knight_dieb8 ] {}; 208 | void() knight_dieb8 =[ $deathb8, knight_dieb9 ] {}; 209 | void() knight_dieb9 =[ $deathb9, knight_dieb10] {}; 210 | void() knight_dieb10 = [ $deathb10, knight_dieb11] {}; 211 | void() knight_dieb11 = [ $deathb11, knight_dieb11] {}; 212 | 213 | 214 | void() knight_die = 215 | { 216 | // check for gib 217 | if (self.health < -40) 218 | { 219 | ThrowHead ("progs/h_knight.mdl", self.health); 220 | ThrowGib ("progs/gib1.mdl", self.health); 221 | ThrowGib ("progs/gib2.mdl", self.health); 222 | ThrowGib ("progs/gib3.mdl", self.health); 223 | // spinstagib sounds 224 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 225 | { 226 | GibSoundsRandom(); 227 | return; 228 | } 229 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 230 | return; 231 | } 232 | 233 | // regular death 234 | sound (self, CHAN_VOICE, "knight/kdeath.wav", 1, ATTN_NORM); 235 | if (random() < 0.5) 236 | knight_die1 (); 237 | else 238 | knight_dieb1 (); 239 | }; 240 | 241 | 242 | /*QUAKED monster_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush 243 | */ 244 | void() monster_knight = 245 | { 246 | if (deathmatch) 247 | { 248 | remove(self); 249 | return; 250 | } 251 | precache_model ("progs/knight.mdl"); 252 | precache_model ("progs/h_knight.mdl"); 253 | 254 | precache_sound ("knight/kdeath.wav"); 255 | precache_sound ("knight/khurt.wav"); 256 | precache_sound ("knight/ksight.wav"); 257 | precache_sound ("knight/sword1.wav"); 258 | precache_sound ("knight/sword2.wav"); 259 | precache_sound ("knight/idle.wav"); 260 | 261 | self.solid = SOLID_SLIDEBOX; 262 | self.movetype = MOVETYPE_STEP; 263 | 264 | setmodel (self, "progs/knight.mdl"); 265 | 266 | setsize (self, '-16 -16 -24', '16 16 40'); 267 | self.health = 75; 268 | 269 | self.th_stand = knight_stand1; 270 | self.th_walk = knight_walk1; 271 | self.th_run = knight_run1; 272 | self.th_melee = knight_atk1; 273 | self.th_pain = knight_pain; 274 | self.th_die = knight_die; 275 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 276 | self.touch = monster_touch; 277 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 278 | 279 | walkmonster_start (); 280 | }; 281 | -------------------------------------------------------------------------------- /src/models.qc: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | =============================================================================== 4 | 5 | WORLD WEAPONS 6 | 7 | =============================================================================== 8 | */ 9 | 10 | $modelname g_shot 11 | $cd id1/models/g_shot 12 | $origin 0 0 -24 13 | $flags 8 // client side rotate 14 | $base base 15 | $skin skin 16 | $frame shot1 17 | 18 | 19 | $modelname g_nail 20 | $cd id1/models/g_nail 21 | $flags 8 // client side rotate 22 | $origin 0 0 -24 23 | $base base 24 | $skin skin 25 | $frame shot1 26 | 27 | 28 | $modelname g_nail2 29 | $cd id1/models/g_nail2 30 | $flags 8 // client side rotate 31 | $origin 0 0 -24 32 | $base base 33 | $skin skin 34 | $frame shot2 35 | 36 | 37 | $modelname g_rock 38 | $cd id1/models/g_rock 39 | $flags 8 // client side rotate 40 | $origin 0 0 -24 41 | $base base 42 | $skin skin 43 | $frame shot1 44 | 45 | 46 | $modelname g_rock2 47 | $cd id1/models/g_rock2 48 | $flags 8 // client side rotate 49 | $origin 0 0 -24 50 | $base base 51 | $skin skin 52 | $frame shot1 53 | 54 | $modelname g_light 55 | $cd id1/models/g_light 56 | $flags 8 // client side rotate 57 | $origin 0 0 -24 58 | $base base 59 | $skin skin 60 | $frame shot1 61 | 62 | /* 63 | =============================================================================== 64 | 65 | VIEW WEAPONS 66 | 67 | =============================================================================== 68 | */ 69 | 70 | $modelname v_axe 71 | $cd id1/models/v_axe 72 | $origin 0 5 54 73 | $base base 74 | $skin skin 75 | $frame frame1 frame2 frame3 frame4 frame5 frame6 frame7 frame8 frame9 76 | 77 | 78 | $modelname v_shot 79 | $cd id1/models/v_shot 80 | $origin 0 0 54 81 | $base base 82 | $skin skin 83 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 84 | 85 | 86 | $modelname v_shot2 87 | $cd id1/models/v_shot2 88 | $origin 0 0 56 89 | $base base 90 | $skin skin 91 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 92 | 93 | 94 | $modelname v_rock2 95 | $cd id1/models/v_rock2 96 | $origin 0 0 54 97 | $base base 98 | $skin skin 99 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot6 100 | 101 | 102 | $modelname v_rock 103 | $cd id1/models/v_rock 104 | $origin 0 0 54 105 | $base base 106 | $skin skin 107 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 108 | 109 | 110 | $modelname v_nail2 111 | $cd id1/models/v_nail2 112 | $origin 0 0 54 113 | $base base 114 | $skin skin 115 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 shot8 shot9 116 | 117 | 118 | $modelname v_nail 119 | $cd id1/models/v_nail 120 | $origin 0 0 54 121 | $base base 122 | $skin skin 123 | $frame shot1 shot2 shot3 shot4 shot5 shot6 shot7 shot8 shot9 124 | 125 | $modelname v_light 126 | $cd id1/models/v_light 127 | $origin 0 0 54 128 | $base base 129 | $skin skin 130 | $frame shot1 shot2 shot3 shot4 shot5 131 | 132 | 133 | /* 134 | =============================================================================== 135 | 136 | ITEMS 137 | 138 | =============================================================================== 139 | */ 140 | 141 | $modelname w_g_key 142 | $cd id1/models/w_g_key 143 | $flags 8 // client side rotate 144 | $base base 145 | $skin skin 146 | $frame frame1 147 | 148 | $modelname w_s_key 149 | $cd id1/models/w_s_key 150 | $flags 8 // client side rotate 151 | $base base 152 | $skin skin 153 | $frame frame1 154 | 155 | $modelname m_g_key 156 | $cd id1/models/m_g_key 157 | $flags 8 // client side rotate 158 | $base base 159 | $skin skin 160 | $frame frame1 161 | 162 | $modelname m_s_key 163 | $cd id1/models/m_s_key 164 | $flags 8 // client side rotate 165 | $base base 166 | $skin skin 167 | $frame frame1 168 | 169 | $modelname b_g_key 170 | $cd id1/models/b_g_key 171 | $flags 8 // client side rotate 172 | $base base 173 | $skin skin 174 | $frame frame1 175 | 176 | $modelname b_s_key 177 | $cd id1/models/b_s_key 178 | $flags 8 // client side rotate 179 | $base base 180 | $skin skin 181 | $frame frame1 182 | 183 | 184 | $modelname quaddama 185 | $cd id1/models/quaddama 186 | $flags 8 // client side rotate 187 | $base base 188 | $skin skin 189 | $frame frame1 190 | 191 | $modelname invisibl 192 | $cd id1/models/invisibl 193 | $flags 8 // client side rotate 194 | $base base 195 | $skin skin 196 | $frame frame1 197 | 198 | $modelname invulner 199 | $flags 8 // client side rotate 200 | $cd id1/models/invulner 201 | $base base 202 | $skin skin 203 | $frame frame1 204 | 205 | //modelname jetpack 206 | //cd id1/models/jetpack 207 | //flags 8 // client side rotate 208 | //base base 209 | //skin skin 210 | //frame frame1 211 | 212 | $modelname cube 213 | $cd id1/models/cube 214 | $flags 8 // client side rotate 215 | $base base 216 | $skin skin 217 | $frame frame1 218 | 219 | $modelname suit 220 | $cd id1/models/suit 221 | $flags 8 // client side rotate 222 | $base base 223 | $skin skin 224 | $frame frame1 225 | 226 | $modelname boots 227 | $cd id1/models/boots 228 | $flags 8 // client side rotate 229 | $base base 230 | $skin skin 231 | $frame frame1 232 | 233 | $modelname end1 234 | $cd id1/models/end1 235 | $flags 8 // client side rotate 236 | $base base 237 | $skin skin 238 | $frame frame1 239 | 240 | $modelname end2 241 | $cd id1/models/end2 242 | $flags 8 // client side rotate 243 | $base base 244 | $skin skin 245 | $frame frame1 246 | 247 | $modelname end3 248 | $cd id1/models/end3 249 | $flags 8 // client side rotate 250 | $base base 251 | $skin skin 252 | $frame frame1 253 | 254 | $modelname end4 255 | $cd id1/models/end4 256 | $flags 8 // client side rotate 257 | $base base 258 | $skin skin 259 | $frame frame1 260 | 261 | 262 | /* 263 | =============================================================================== 264 | 265 | GIBS 266 | 267 | =============================================================================== 268 | */ 269 | 270 | $modelname gib1 271 | $cd id1/models/gib1 272 | $flags 4 // EF_GIB 273 | $origin 0 0 0 274 | $base base 275 | $skin skin 276 | $frame frame1 277 | 278 | 279 | // torso 280 | $modelname gib2 281 | $cd id1/models/gib2 282 | $flags 4 // EF_GIB 283 | $origin 0 0 0 284 | $base base 285 | $skin skin 286 | $frame frame1 287 | 288 | $modelname gib3 289 | $cd id1/models/gib3 290 | $flags 4 // EF_GIB 291 | $origin 0 0 0 292 | $base base 293 | $skin skin 294 | $frame frame1 295 | 296 | 297 | // heads 298 | 299 | $modelname h_player 300 | $cd id1/models/h_player 301 | $flags 4 // EF_GIB 302 | $origin 0 0 0 303 | $base base 304 | $skin skin 305 | $frame frame1 306 | 307 | $modelname h_dog 308 | $cd id1/models/h_dog 309 | $flags 4 // EF_GIB 310 | $origin 0 0 0 311 | $base base 312 | $skin skin 313 | $frame frame1 314 | 315 | $modelname h_mega 316 | $cd id1/models/h_mega 317 | $flags 4 // EF_GIB 318 | $origin 0 0 0 319 | $base base 320 | $skin skin 321 | $frame frame1 322 | 323 | $modelname h_guard 324 | $cd id1/models/h_guard 325 | $flags 4 // EF_GIB 326 | $origin 0 0 0 327 | $base base 328 | $skin skin 329 | $frame frame1 330 | 331 | $modelname h_wizard 332 | $cd id1/models/h_wizard 333 | $flags 4 // EF_GIB 334 | $origin 0 0 0 335 | $base base 336 | $skin skin 337 | $frame frame1 338 | 339 | $modelname h_knight 340 | $cd id1/models/h_knight 341 | $flags 4 // EF_GIB 342 | $origin 0 0 0 343 | $base base 344 | $skin skin 345 | $frame frame1 346 | 347 | $modelname h_hellkn 348 | $cd id1/models/h_hellkn 349 | $flags 4 // EF_GIB 350 | $origin 0 0 0 351 | $base base 352 | $skin skin 353 | $frame frame1 354 | 355 | $modelname h_zombie 356 | $cd id1/models/h_zombie 357 | $flags 4 // EF_GIB 358 | $origin 0 0 0 359 | $base base 360 | $skin skin 361 | $frame frame1 362 | 363 | $modelname h_shams 364 | $cd id1/models/h_shams 365 | $flags 4 // EF_GIB 366 | $origin 0 0 0 367 | $base base 368 | $skin skin 369 | $frame frame1 370 | 371 | $modelname h_shal 372 | $cd id1/models/h_shal 373 | $flags 4 // EF_GIB 374 | $origin 0 0 0 375 | $base base 376 | $skin skin 377 | $frame frame1 378 | 379 | $modelname h_ogre 380 | $cd id1/models/h_ogre 381 | $flags 4 // EF_GIB 382 | $origin 0 0 0 383 | $base base 384 | $skin skin 385 | $frame frame1 386 | 387 | $modelname h_demon 388 | $cd id1/models/h_demon 389 | $flags 4 // EF_GIB 390 | $origin 0 0 0 391 | $base base 392 | $skin skin 393 | $frame frame1 394 | 395 | /* 396 | =============================================================================== 397 | 398 | MISC 399 | 400 | =============================================================================== 401 | */ 402 | 403 | $modelname armor 404 | $cd id1/models/armor 405 | $flags 8 // client side rotate 406 | $origin 0 0 -8 407 | $base base 408 | $skin skin 409 | $skin skin2 410 | $skin skin3 411 | $frame armor 412 | 413 | $modelname s_light // shambler lightning ready 414 | $cd id1/models/s_light 415 | $origin 0 0 24 416 | $base base 417 | $skin skin 418 | $frame frame1 frame2 frame3 419 | 420 | $modelname bolt3 // lightning towar bolts 421 | $cd id1/models/bolt2 422 | $origin 0 0 0 423 | $base base 424 | $scale 4 425 | $skin skin 426 | $frame light 427 | 428 | $modelname bolt2 429 | $cd id1/models/bolt2 430 | $origin 0 0 0 431 | $base base 432 | $skin skin 433 | $frame light 434 | 435 | $modelname bolt 436 | $cd id1/models/bolt 437 | $origin 0 0 0 438 | $base light 439 | $skin light 440 | $frame light 441 | 442 | $modelname laser 443 | $cd id1/models/laser 444 | $base base 445 | $skin skin 446 | $scale 2 447 | $frame frame1 448 | 449 | $modelname flame // with torch 450 | $cd id1/models/flame 451 | $origin 0 0 12 452 | $base base 453 | $skin skin 454 | $framegroupstart 455 | $frame flame1 0.1 456 | $frame flame2 0.1 457 | $frame flame3 0.1 458 | $frame flame4 0.1 459 | $frame flame5 0.1 460 | $frame flame6 0.1 461 | $framegroupend 462 | 463 | $modelname flame2 // standing flame, no torch 464 | $cd id1/models/flame2 465 | $origin 0 0 12 466 | $base base 467 | $skin skin 468 | $framegroupstart 469 | $frame flame1 0.1 470 | $frame flame2 0.1 471 | $frame flame3 0.1 472 | $frame flame4 0.1 473 | $frame flame5 0.1 474 | $frame flame6 0.1 475 | $framegroupend 476 | $framegroupstart 477 | $frame flameb1 478 | $frame flameb2 479 | $frame flameb3 480 | $frame flameb4 481 | $frame flameb5 482 | $frame flameb6 483 | $frame flameb7 484 | $frame flameb8 485 | $frame flameb9 486 | $frame flameb10 487 | $frame flameb11 488 | $framegroupend 489 | 490 | $modelname zom_gib 491 | $cd id1/models/zom_gib 492 | $flags 32 // EF_ZOMGIB 493 | $base base 494 | $skin skin 495 | $frame frame1 496 | 497 | $modelname eyes 498 | $cd id1/models/eyes 499 | $origin 0 0 -24 500 | $base base 501 | $skin skin 502 | $frame frame1 503 | 504 | $modelname spike 505 | $cd id1/models/spike 506 | $origin 0 0 0 507 | $base spike 508 | $skin skin 509 | $frame spike 510 | 511 | $modelname s_spike 512 | $cd id1/models/s_spike 513 | $origin 0 0 0 514 | $base spike 515 | $skin skin 516 | $frame spike 517 | 518 | $modelname v_spike 519 | $cd id1/models/v_spike 520 | $flags 128 // EF_TRACER3 521 | $origin 0 0 0 522 | $base base 523 | $skin skin 524 | $frame frame1 525 | 526 | $modelname w_spike 527 | $cd id1/models/w_spike 528 | $flags 16 // EF_TRACER 529 | $origin 0 0 0 530 | $base base 531 | $skin skin 532 | $framegroupstart 533 | $frame frame1 0.1 534 | $frame frame2 0.1 535 | $frame frame3 0.1 536 | $frame frame4 0.1 537 | $framegroupend 538 | 539 | $modelname k_spike 540 | $cd id1/models/k_spike 541 | $flags 64 // EF_TRACER2 542 | $origin 0 0 0 543 | $base base 544 | $skin skin 545 | $frame frame1 546 | 547 | $modelname backpack 548 | $cd id1/models/backpack 549 | $flags 8 // EF_ROTATE 550 | $origin 0 0 0 551 | $base base 552 | $skin skin 553 | $frame frame1 554 | 555 | $modelname grenade 556 | $cd id1/models/grenade2 557 | $flags 2 // EF_GRENADE 558 | $origin 0 0 0 559 | $base base 560 | $skin skin 561 | $frame grenade 562 | 563 | $modelname missile 564 | $cd id1/models/missile 565 | $flags 1 // EF_ROCKET 566 | $origin 0 0 0 567 | $base base 568 | $skin skin 569 | $frame missile 570 | 571 | $modelname lavaball 572 | $cd id1/models/lavaball 573 | $flags 1 // EF_ROCKET 574 | $origin 0 0 0 575 | $base base 576 | $skin skin 577 | $frame frame1 578 | 579 | $modelname teleport 580 | $cd id1/models/teleport 581 | $origin 0 0 24 582 | $base base 583 | $skin skin 584 | $frame frame1 585 | 586 | -------------------------------------------------------------------------------- /src/monsters.qc: -------------------------------------------------------------------------------- 1 | /* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */ 2 | 3 | // name =[framenum, nexttime, nextthink] {code} 4 | // expands to: 5 | // name () 6 | // { 7 | // self.frame=framenum; 8 | // self.nextthink = time + nexttime; 9 | // self.think = nextthink 10 | // 11 | // }; 12 | // 13 | 14 | /* 15 | ================ 16 | monster_update_total 17 | 18 | Call this function to safely update total_monsters when the game is in 19 | progress. It adds "n" to total_monsters, then notifies all clients of 20 | the change. -- iw 21 | ================ 22 | */ 23 | void(float n) monster_update_total = 24 | { 25 | total_monsters = total_monsters + n; 26 | 27 | WriteByte (MSG_ALL, SVC_UPDATESTAT); 28 | WriteByte (MSG_ALL, STAT_TOTALMONSTERS); 29 | WriteLong (MSG_ALL, total_monsters); 30 | }; 31 | 32 | // Preach's teleporting monsters flag - start -- dumptruck_ds 33 | // from: https://tomeofpreach.wordpress.com/2017/10/08/teleporting-monsters-flag/ 34 | // 35 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 36 | /* 37 | ================ 38 | by: Philip Martin aka: Kryten 39 | When on top of monsters or players you slide. This is a QuakeC problem. 40 | The function below fixes that problem. 41 | based on code given to Kryten by: Michael Turitzin (MaNiAc) 42 | ================ 43 | */ 44 | void() monster_touch = 45 | { 46 | //can cause problems for monsters on top of a player, so only players 47 | if (other.classname != "player") 48 | return; 49 | if (other.health <= 0) 50 | return; 51 | 52 | if ((!(other.flags & FL_ONGROUND)) && (other.absmin_z >= self.absmax_z - 2)) 53 | other.flags = other.flags + FL_ONGROUND; 54 | 55 | //you can add other stuff like pushable players/monsters here 56 | }; 57 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 58 | 59 | .string tele_model; 60 | .vector tele_mins; 61 | .vector tele_maxs; 62 | .float tele_solid; 63 | .float tele_movetype; 64 | void(vector org) spawn_tfog; 65 | void(vector org, entity death_owner) spawn_tdeath; 66 | 67 | void() monster_teleport_go = 68 | { 69 | self.solid = self.tele_solid; 70 | self.movetype = self.tele_movetype; 71 | setmodel(self, self.tele_model); 72 | setsize (self, self.tele_mins, self.tele_maxs); 73 | 74 | self.think1(); 75 | //PREACH: override the random delay some go functions apply 76 | self.nextthink = time + 0.1; 77 | if !(self.spawnflags & 32) // for trigger spawning dumptruck_ds 78 | spawn_tfog (self.origin); 79 | spawn_tdeath(self.origin, self); 80 | } 81 | 82 | /* 83 | ================ 84 | monster_teleport_check 85 | 86 | This detects and eliminates a common map bug: a trigger-spawned monster 87 | which can't be activated either because it has no targetname or because 88 | its targetname isn't targeted by any other entity. (This map bug would 89 | otherwise make it impossible for the player to get 100% kills.) -- iw 90 | ================ 91 | */ 92 | void() monster_teleport_check = 93 | { 94 | if (!SUB_IsTargeted ()) 95 | { 96 | dprint ("WARNING: removed untargeted trigger-spawned "); 97 | dprint (self.classname); 98 | dprint (" at "); 99 | dprint (vtos (self.origin)); 100 | dprint ("\n"); 101 | 102 | remove (self); 103 | return; 104 | } 105 | 106 | // the targetname appears to be OK, so let's finish setting up the 107 | // trigger-spawned monster -- iw 108 | monster_update_total (1); 109 | }; 110 | 111 | float (void() monster_start_fn) monster_teleport = 112 | { 113 | if(!(self.spawnflags & 8)) 114 | return FALSE; 115 | 116 | //PREACH: This monster is to be teleported in, so hide it 117 | self.tele_model= self.model; 118 | self.tele_mins = self.mins; 119 | self.tele_maxs = self.maxs; 120 | self.tele_solid = self.solid; 121 | self.tele_movetype = self.movetype; 122 | 123 | self.model = ""; 124 | self.modelindex = 0; 125 | self.solid = SOLID_NOT; 126 | self.movetype = MOVETYPE_NONE; 127 | self.use = monster_teleport_go; 128 | self.think1 = monster_start_fn; 129 | 130 | // wait for other entities to finish spawning, then check that 131 | // something targets this -- iw 132 | self.think = monster_teleport_check; 133 | self.nextthink = time + 0.1; 134 | 135 | return TRUE; 136 | } 137 | // Preach's teleporting monsters flag - end -- dumptruck_ds 138 | /* 139 | ================ 140 | monster_use 141 | 142 | Using a monster makes it angry at the current activator 143 | ================ 144 | */ 145 | void() monster_use = 146 | { 147 | if (self.enemy) 148 | return; 149 | if (self.health <= 0) 150 | return; 151 | if (activator.items & IT_INVISIBILITY) 152 | return; 153 | if (activator.flags & FL_NOTARGET) 154 | return; 155 | if (activator.classname != "player") 156 | return; 157 | 158 | // delay reaction so if the monster is teleported, its sound is still 159 | // heard 160 | self.enemy = activator; 161 | self.nextthink = time + 0.1; 162 | self.think = FoundTarget; 163 | }; 164 | 165 | /* 166 | ================ 167 | monster_death_use 168 | 169 | When a mosnter dies, it fires all of its targets with the current 170 | enemy as activator. 171 | ================ 172 | */ 173 | void() monster_death_use = 174 | { 175 | 176 | // fall to ground 177 | if (self.flags & FL_FLY) 178 | self.flags = self.flags - FL_FLY; 179 | if (self.flags & FL_SWIM) 180 | self.flags = self.flags - FL_SWIM; 181 | 182 | // -- this fixes an error. More info here: https://www.celephais.net/board/view_thread.php?id=4&start=7447 183 | // if (!self.target) 184 | // return; 185 | 186 | activator = self.enemy; 187 | SUB_UseTargets (); 188 | }; 189 | 190 | 191 | //============================================================================ 192 | 193 | void() walkmonster_start_go = 194 | { 195 | self.origin_z = self.origin_z + 1; // raise off floor a bit 196 | // Preach's teleporting monsters flag - start -- dumptruck_ds 197 | if(time <= 0.5) 198 | { 199 | droptofloor(); 200 | 201 | if (!walkmove(0,0)) 202 | { 203 | dprint ("walkmonster in wall at: "); 204 | dprint (vtos(self.origin)); 205 | dprint ("\n"); 206 | } 207 | } 208 | // Preach's teleporting monsters flag - end -- dumptruck_ds 209 | 210 | self.takedamage = DAMAGE_AIM; 211 | 212 | self.ideal_yaw = self.angles * '0 1 0'; 213 | if (!self.yaw_speed) 214 | self.yaw_speed = 20; 215 | self.view_ofs = '0 0 25'; 216 | self.use = monster_use; 217 | 218 | self.flags = self.flags | FL_MONSTER; 219 | 220 | if (self.target) 221 | { 222 | self.goalentity = self.movetarget = find(world, targetname, self.target); 223 | self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); 224 | if (!self.movetarget) 225 | { 226 | dprint ("Monster can't find target at "); 227 | dprint (vtos(self.origin)); 228 | dprint ("\n"); 229 | } 230 | // this used to be an objerror 231 | if (self.movetarget.classname == "path_corner") 232 | self.th_walk (); 233 | else 234 | self.pausetime = 99999999; 235 | self.th_stand (); 236 | } 237 | else 238 | { 239 | self.pausetime = 99999999; 240 | self.th_stand (); 241 | } 242 | 243 | // spread think times so they don't all happen at same time 244 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol start 245 | // self.nextthink = self.nextthink + random()*0.5; 246 | self.nextthink = time + 0.1 + random()*0.5; 247 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol end 248 | local entity pl; //Spawn Angry method from Shamblernaut - dumptruck_ds 249 | 250 | pl = find (world, classname, "player"); 251 | 252 | if (self.spawnflags & SPAWN_ANGRY) 253 | { 254 | activator = pl; 255 | monster_use(); 256 | } 257 | }; 258 | 259 | 260 | void() walkmonster_start = 261 | { 262 | // Preach's teleporting monsters flag - start -- dumptruck_ds 263 | if(monster_teleport(walkmonster_start_go)) 264 | return; 265 | // Preach's teleporting monsters flag - end -- dumptruck_ds 266 | 267 | self.touch = monster_touch; // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten 268 | 269 | // delay drop to floor to make sure all doors have been spawned 270 | // spread think times so they don't all happen at same time 271 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol start 272 | // self.nextthink = self.nextthink + random()*0.5; 273 | self.nextthink = time + 0.1 + random()*0.5; 274 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol end 275 | self.think = walkmonster_start_go; 276 | total_monsters = total_monsters + 1; 277 | }; 278 | 279 | 280 | 281 | void() flymonster_start_go = 282 | { 283 | self.takedamage = DAMAGE_AIM; 284 | 285 | self.ideal_yaw = self.angles * '0 1 0'; 286 | if (!self.yaw_speed) 287 | self.yaw_speed = 10; 288 | self.view_ofs = '0 0 25'; 289 | self.use = monster_use; 290 | 291 | self.flags = self.flags | FL_FLY; 292 | self.flags = self.flags | FL_MONSTER; 293 | 294 | if (!walkmove(0,0)) 295 | { 296 | dprint ("flymonster in wall at: "); 297 | dprint (vtos(self.origin)); 298 | dprint ("\n"); 299 | } 300 | 301 | if (self.target) 302 | { 303 | self.goalentity = self.movetarget = find(world, targetname, self.target); 304 | if (!self.movetarget) 305 | { 306 | dprint ("Monster can't find target at "); 307 | dprint (vtos(self.origin)); 308 | dprint ("\n"); 309 | } 310 | // this used to be an objerror 311 | if (self.movetarget.classname == "path_corner") 312 | self.th_walk (); 313 | else 314 | self.pausetime = 99999999; 315 | self.th_stand (); 316 | } 317 | else 318 | { 319 | self.pausetime = 99999999; 320 | self.th_stand (); 321 | } 322 | self.nextthink = time + 0.1 + random()*0.5; // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol 323 | local entity pl; //Spawn Angry method from Shamblernaut - dumptruck_ds 324 | 325 | pl = find (world, classname, "player"); 326 | 327 | if (self.spawnflags & SPAWN_ANGRY) 328 | { 329 | activator = pl; 330 | monster_use(); 331 | } 332 | }; 333 | 334 | void() flymonster_start = 335 | { 336 | // Preach's teleporting monsters flag - start -- dumptruck_ds 337 | if(monster_teleport(flymonster_start_go)) 338 | return; 339 | 340 | self.touch = monster_touch; // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten 341 | // Preach's teleporting monsters flag - end -- dumptruck_ds 342 | self.flags = self.flags | FL_FLY; // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol 343 | // spread think times so they don't all happen at same time 344 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol start 345 | // self.nextthink = self.nextthink + random()*0.5; 346 | self.nextthink = time + 0.1 + random()*0.5; 347 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol end 348 | self.think = flymonster_start_go; 349 | total_monsters = total_monsters + 1; 350 | }; 351 | 352 | void() swimmonster_start_go = 353 | { 354 | if (deathmatch) 355 | { 356 | remove(self); 357 | return; 358 | } 359 | 360 | self.takedamage = DAMAGE_AIM; 361 | //total_monsters = total_monsters + 1; fish count fix -- dumptruck_ds 362 | 363 | self.ideal_yaw = self.angles * '0 1 0'; 364 | if (!self.yaw_speed) 365 | self.yaw_speed = 10; 366 | self.view_ofs = '0 0 10'; 367 | self.use = monster_use; 368 | 369 | self.flags = self.flags | FL_SWIM; 370 | self.flags = self.flags | FL_MONSTER; 371 | 372 | if (self.target) 373 | { 374 | self.goalentity = self.movetarget = find(world, targetname, self.target); 375 | if (!self.movetarget) 376 | { 377 | dprint ("Monster can't find target at "); 378 | dprint (vtos(self.origin)); 379 | dprint ("\n"); 380 | } 381 | // this used to be an objerror 382 | self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); 383 | self.th_walk (); 384 | } 385 | else 386 | { 387 | self.pausetime = 99999999; 388 | self.th_stand (); 389 | } 390 | 391 | // spread think times so they don't all happen at same time 392 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol start 393 | // self.nextthink = self.nextthink + random()*0.5; 394 | self.nextthink = time + 0.1 + random()*0.5; 395 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol end 396 | local entity pl; //Spawn Angry method from Shamblernaut - dumptruck_ds 397 | 398 | pl = find (world, classname, "player"); 399 | 400 | if (self.spawnflags & SPAWN_ANGRY) 401 | { 402 | activator = pl; 403 | monster_use(); 404 | } 405 | }; 406 | 407 | void() swimmonster_start = 408 | { 409 | self.touch = monster_touch; // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten 410 | 411 | self.flags = self.flags | FL_SWIM; // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol 412 | // Preach's teleporting monsters flag - start -- dumptruck_ds 413 | if(monster_teleport(swimmonster_start_go)) 414 | return; 415 | // Preach's teleporting monsters flag - end -- dumptruck_ds 416 | 417 | // spread think times so they don't all happen at same time 418 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol start 419 | // self.nextthink = self.nextthink + random()*0.5; 420 | self.nextthink = time + 0.1 + random()*0.5; 421 | // 1998-08-14 Monsters sometimes do not move fix by Lord Sméagol end 422 | self.think = swimmonster_start_go; 423 | total_monsters = total_monsters + 1; 424 | }; 425 | -------------------------------------------------------------------------------- /src/oldone.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | OLD ONE 5 | 6 | ============================================================================== 7 | */ 8 | $cd id1/models/old_one 9 | $origin 0 0 24 10 | $base base 11 | $skin skin 12 | $scale 1 13 | 14 | void() finale_1; 15 | void() finale_2; 16 | void() finale_3; 17 | void() finale_4; 18 | 19 | 20 | entity shub; 21 | 22 | $frame old1 old2 old3 old4 old5 old6 old7 old8 old9 23 | $frame old10 old11 old12 old13 old14 old15 old16 old17 old18 old19 24 | $frame old20 old21 old22 old23 old24 old25 old26 old27 old28 old29 25 | $frame old30 old31 old32 old33 old34 old35 old36 old37 old38 old39 26 | $frame old40 old41 old42 old43 old44 old45 old46 27 | 28 | $frame shake1 shake2 shake3 shake4 shake5 shake6 shake7 shake8 29 | $frame shake9 shake10 shake11 shake12 shake13 shake14 30 | $frame shake15 shake16 shake17 shake18 shake19 shake20 31 | 32 | //void() old_stand =[ $old1, old_stand ] {}; 33 | 34 | void() old_idle1 =[ $old1, old_idle2 ] {}; 35 | void() old_idle2 =[ $old2, old_idle3 ] {}; 36 | void() old_idle3 =[ $old3, old_idle4 ] {}; 37 | void() old_idle4 =[ $old4, old_idle5 ] {}; 38 | void() old_idle5 =[ $old5, old_idle6 ] {}; 39 | void() old_idle6 =[ $old6, old_idle7 ] {}; 40 | void() old_idle7 =[ $old7, old_idle8 ] {}; 41 | void() old_idle8 =[ $old8, old_idle9 ] {}; 42 | void() old_idle9 =[ $old9, old_idle10 ] {}; 43 | void() old_idle10 =[ $old10, old_idle11 ] {}; 44 | void() old_idle11 =[ $old11, old_idle12 ] {}; 45 | void() old_idle12 =[ $old12, old_idle13 ] {}; 46 | void() old_idle13 =[ $old13, old_idle14 ] {}; 47 | void() old_idle14 =[ $old14, old_idle15 ] {}; 48 | void() old_idle15 =[ $old15, old_idle16 ] {}; 49 | void() old_idle16 =[ $old16, old_idle17 ] {}; 50 | void() old_idle17 =[ $old17, old_idle18 ] {}; 51 | void() old_idle18 =[ $old18, old_idle19 ] {}; 52 | void() old_idle19 =[ $old19, old_idle20 ] {}; 53 | void() old_idle20 =[ $old20, old_idle21 ] {}; 54 | void() old_idle21 =[ $old21, old_idle22 ] {}; 55 | void() old_idle22 =[ $old22, old_idle23 ] {}; 56 | void() old_idle23 =[ $old23, old_idle24 ] {}; 57 | void() old_idle24 =[ $old24, old_idle25 ] {}; 58 | void() old_idle25 =[ $old25, old_idle26 ] {}; 59 | void() old_idle26 =[ $old26, old_idle27 ] {}; 60 | void() old_idle27 =[ $old27, old_idle28 ] {}; 61 | void() old_idle28 =[ $old28, old_idle29 ] {}; 62 | void() old_idle29 =[ $old29, old_idle30 ] {}; 63 | void() old_idle30 =[ $old30, old_idle31 ] {}; 64 | void() old_idle31 =[ $old31, old_idle32 ] {}; 65 | void() old_idle32 =[ $old32, old_idle33 ] {}; 66 | void() old_idle33 =[ $old33, old_idle34 ] {}; 67 | void() old_idle34 =[ $old34, old_idle35 ] {}; 68 | void() old_idle35 =[ $old35, old_idle36 ] {}; 69 | void() old_idle36 =[ $old36, old_idle37 ] {}; 70 | void() old_idle37 =[ $old37, old_idle38 ] {}; 71 | void() old_idle38 =[ $old38, old_idle39 ] {}; 72 | void() old_idle39 =[ $old39, old_idle40 ] {}; 73 | void() old_idle40 =[ $old40, old_idle41 ] {}; 74 | void() old_idle41 =[ $old41, old_idle42 ] {}; 75 | void() old_idle42 =[ $old42, old_idle43 ] {}; 76 | void() old_idle43 =[ $old43, old_idle44 ] {}; 77 | void() old_idle44 =[ $old44, old_idle45 ] {}; 78 | void() old_idle45 =[ $old45, old_idle46 ] {}; 79 | void() old_idle46 =[ $old46, old_idle1 ] {}; 80 | 81 | 82 | void() old_thrash1 =[ $shake1, old_thrash2 ] {lightstyle(0, "m");}; 83 | void() old_thrash2 =[ $shake2, old_thrash3 ] {lightstyle(0, "k");}; 84 | void() old_thrash3 =[ $shake3, old_thrash4 ] {lightstyle(0, "k");}; 85 | void() old_thrash4 =[ $shake4, old_thrash5 ] {lightstyle(0, "i");}; 86 | void() old_thrash5 =[ $shake5, old_thrash6 ] {lightstyle(0, "g");}; 87 | void() old_thrash6 =[ $shake6, old_thrash7 ] {lightstyle(0, "e");}; 88 | void() old_thrash7 =[ $shake7, old_thrash8 ] {lightstyle(0, "c");}; 89 | void() old_thrash8 =[ $shake8, old_thrash9 ] {lightstyle(0, "a");}; 90 | void() old_thrash9 =[ $shake9, old_thrash10 ] {lightstyle(0, "c");}; 91 | void() old_thrash10 =[ $shake10, old_thrash11 ] {lightstyle(0, "e");}; 92 | void() old_thrash11 =[ $shake11, old_thrash12 ] {lightstyle(0, "g");}; 93 | void() old_thrash12 =[ $shake12, old_thrash13 ] {lightstyle(0, "i");}; 94 | void() old_thrash13 =[ $shake13, old_thrash14 ] {lightstyle(0, "k");}; 95 | void() old_thrash14 =[ $shake14, old_thrash15 ] {lightstyle(0, "m");}; 96 | void() old_thrash15 =[ $shake15, old_thrash16 ] {lightstyle(0, "m"); 97 | self.cnt = self.cnt + 1; 98 | if (self.cnt != 3) 99 | self.think = old_thrash1; 100 | }; 101 | void() old_thrash16 =[ $shake16, old_thrash17 ] {lightstyle(0, "g");}; 102 | void() old_thrash17 =[ $shake17, old_thrash18 ] {lightstyle(0, "c");}; 103 | void() old_thrash18 =[ $shake18, old_thrash19 ] {lightstyle(0, "b");}; 104 | void() old_thrash19 =[ $shake19, old_thrash20 ] {lightstyle(0, "a");}; 105 | void() old_thrash20 =[ $shake20, old_thrash20 ] {finale_4();}; 106 | 107 | //============================================================================ 108 | 109 | void() finale_1 = 110 | { 111 | local entity pos, pl; 112 | local entity timer; 113 | // 1998-07-30 Shub kill count fix by Maddes start 114 | killed_monsters = killed_monsters + 1; 115 | WriteByte (MSG_ALL, SVC_KILLEDMONSTER); // FIXME: reliable broadcast 116 | // 1998-07-30 Shub kill count fix by Maddes end 117 | intermission_exittime = time + 10000000; // never allow exit 118 | intermission_running = 1; 119 | 120 | // find the intermission spot 121 | pos = find (world, classname, "info_intermission"); 122 | if (!pos) 123 | error ("no info_intermission"); 124 | pl = find (world, classname, "misc_teleporttrain"); 125 | if (!pl) 126 | error ("no teleporttrain"); 127 | remove (pl); 128 | 129 | WriteByte (MSG_ALL, SVC_FINALE); 130 | WriteString (MSG_ALL, ""); 131 | 132 | pl = find (world, classname, "player"); 133 | while (pl != world) 134 | { 135 | pl.view_ofs = '0 0 0'; 136 | pl.angles = other.v_angle = pos.mangle; 137 | pl.fixangle = TRUE; // turn this way immediately 138 | pl.map = self.map; 139 | pl.nextthink = time + 0.5; 140 | pl.takedamage = DAMAGE_NO; 141 | pl.solid = SOLID_NOT; 142 | pl.movetype = MOVETYPE_NONE; 143 | pl.modelindex = 0; 144 | setorigin (pl, pos.origin); 145 | pl = find (pl, classname, "player"); 146 | } 147 | 148 | // make fake versions of all players as standins, and move the real 149 | // players to the intermission spot 150 | 151 | // wait for 1 second 152 | timer = spawn(); 153 | timer.nextthink = time + 1; 154 | timer.think = finale_2; 155 | }; 156 | 157 | void() finale_2 = 158 | { 159 | local vector o; 160 | 161 | // start a teleport splash inside shub 162 | 163 | o = shub.origin - '0 100 0'; 164 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 165 | WriteByte (MSG_BROADCAST, TE_TELEPORT); 166 | WriteCoord (MSG_BROADCAST, o_x); 167 | WriteCoord (MSG_BROADCAST, o_y); 168 | WriteCoord (MSG_BROADCAST, o_z); 169 | 170 | sound (shub, CHAN_VOICE, "misc/r_tele1.wav", 1, ATTN_NORM); 171 | 172 | self.nextthink = time + 2; 173 | self.think = finale_3; 174 | }; 175 | 176 | void() finale_3 = 177 | { 178 | // start shub thrashing wildly 179 | shub.think = old_thrash1; 180 | sound (shub, CHAN_VOICE, "boss2/death.wav", 1, ATTN_NORM); 181 | lightstyle(0, "abcdefghijklmlkjihgfedcb"); 182 | }; 183 | 184 | void() finale_4 = 185 | { 186 | // throw tons of meat chunks 187 | local vector oldo; 188 | local float x, y, z; 189 | local float r; 190 | local entity n; 191 | 192 | sound (self, CHAN_VOICE, "boss2/pop2.wav", 1, ATTN_NORM); 193 | 194 | oldo = self.origin; 195 | 196 | z = 16; 197 | while (z <= 144) 198 | { 199 | x = -64; 200 | while (x <= 64) 201 | { 202 | y = -64; 203 | while (y <= 64) 204 | { 205 | self.origin_x = oldo_x + x; 206 | self.origin_y = oldo_y + y; 207 | self.origin_z = oldo_z + z; 208 | 209 | r = random(); 210 | if (r < 0.3) 211 | ThrowGib ("progs/gib1.mdl", -999); 212 | else if (r < 0.6) 213 | ThrowGib ("progs/gib2.mdl", -999); 214 | else 215 | ThrowGib ("progs/gib3.mdl", -999); 216 | y = y + 32; 217 | } 218 | x = x + 32; 219 | } 220 | z = z + 96; 221 | } 222 | // start the end text 223 | WriteByte (MSG_ALL, SVC_FINALE); 224 | WriteString (MSG_ALL, "Congratulations and well done! You have\nbeaten the hideous Shub-Niggurath, and\nher hundreds of ugly changelings and\nmonsters. You have proven that your\nskill and your cunning are greater than\nall the powers of Quake. You are the\nmaster now. Id Software salutes you."); 225 | 226 | // put a player model down 227 | n = spawn(); 228 | setmodel (n, "progs/player.mdl"); 229 | oldo = oldo - '32 264 0'; 230 | setorigin (n, oldo); 231 | n.angles = '0 290 0'; 232 | n.frame = 1; 233 | 234 | remove (self); 235 | 236 | // switch cd track 237 | WriteByte (MSG_ALL, SVC_CDTRACK); 238 | WriteByte (MSG_ALL, 3); 239 | WriteByte (MSG_ALL, 3); 240 | lightstyle(0, "m"); 241 | }; 242 | 243 | //============================================================================ 244 | 245 | void (entity attacker, float damage) nopain = // compiler warning fixes from mod_jam_progs -- dumptruck_ds 246 | { 247 | self.health = 40000; 248 | }; 249 | 250 | //============================================================================ 251 | 252 | 253 | /*QUAKED monster_oldone (1 0 0) (-16 -16 -24) (16 16 32) 254 | */ 255 | void() monster_oldone = 256 | { 257 | if (deathmatch) 258 | { 259 | remove(self); 260 | return; 261 | } 262 | 263 | precache_model2 ("progs/oldone.mdl"); 264 | 265 | precache_sound2 ("boss2/death.wav"); 266 | precache_sound2 ("boss2/idle.wav"); 267 | precache_sound2 ("boss2/sight.wav"); 268 | precache_sound2 ("boss2/pop2.wav"); 269 | 270 | self.solid = SOLID_SLIDEBOX; 271 | self.movetype = MOVETYPE_STEP; 272 | 273 | setmodel (self, "progs/oldone.mdl"); 274 | setsize (self, '-160 -128 -24', '160 128 256'); 275 | 276 | self.health = 40000; // kill by telefrag 277 | self.think = old_idle1; 278 | self.nextthink = time + 0.1; 279 | self.takedamage = DAMAGE_YES; 280 | self.th_pain = nopain; 281 | self.th_die = finale_1; 282 | self.touch = monster_touch; // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten 283 | shub = self; 284 | 285 | total_monsters = total_monsters + 1; 286 | }; 287 | -------------------------------------------------------------------------------- /src/plats.qc: -------------------------------------------------------------------------------- 1 | 2 | 3 | void() plat_center_touch; 4 | void() plat_outside_touch; 5 | void() plat_trigger_use; 6 | void() plat_go_up; 7 | void() plat_go_down; 8 | void() plat_crush; 9 | float PLAT_LOW_TRIGGER = 1; 10 | 11 | void() plat_spawn_inside_trigger = 12 | { 13 | local entity trigger; 14 | local vector tmin, tmax; 15 | 16 | // 17 | // middle trigger 18 | // 19 | trigger = spawn(); 20 | trigger.touch = plat_center_touch; 21 | trigger.movetype = MOVETYPE_NONE; 22 | trigger.solid = SOLID_TRIGGER; 23 | trigger.enemy = self; 24 | 25 | tmin = self.mins + '25 25 0'; 26 | tmax = self.maxs - '25 25 -8'; 27 | tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); 28 | if (self.spawnflags & PLAT_LOW_TRIGGER) 29 | tmax_z = tmin_z + 8; 30 | 31 | if (self.size_x <= 50) 32 | { 33 | tmin_x = (self.mins_x + self.maxs_x) / 2; 34 | tmax_x = tmin_x + 1; 35 | } 36 | if (self.size_y <= 50) 37 | { 38 | tmin_y = (self.mins_y + self.maxs_y) / 2; 39 | tmax_y = tmin_y + 1; 40 | } 41 | 42 | setsize (trigger, tmin, tmax); 43 | }; 44 | 45 | void() plat_hit_top = 46 | { 47 | sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); 48 | self.state = STATE_TOP; 49 | self.think = plat_go_down; 50 | self.nextthink = self.ltime + 3; 51 | }; 52 | 53 | void() plat_hit_bottom = 54 | { 55 | sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); 56 | self.state = STATE_BOTTOM; 57 | }; 58 | 59 | void() plat_go_down = 60 | { 61 | sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); 62 | self.state = STATE_DOWN; 63 | SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom); 64 | }; 65 | 66 | void() plat_go_up = 67 | { 68 | sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); 69 | self.state = STATE_UP; 70 | SUB_CalcMove (self.pos1, self.speed, plat_hit_top); 71 | }; 72 | 73 | void() plat_center_touch = 74 | { 75 | if (other.classname != "player") 76 | return; 77 | 78 | if (other.health <= 0) 79 | return; 80 | 81 | self = self.enemy; 82 | if (self.state == STATE_BOTTOM) 83 | plat_go_up (); 84 | else if (self.state == STATE_TOP) 85 | self.nextthink = self.ltime + 1; // delay going down 86 | }; 87 | 88 | void() plat_outside_touch = 89 | { 90 | if (other.classname != "player") 91 | return; 92 | 93 | if (other.health <= 0) 94 | return; 95 | 96 | //dprint ("plat_outside_touch\n"); 97 | self = self.enemy; 98 | if (self.state == STATE_TOP) 99 | plat_go_down (); 100 | }; 101 | 102 | void() plat_trigger_use = 103 | { 104 | if (self.think) 105 | return; // allready activated 106 | plat_go_down(); 107 | }; 108 | 109 | 110 | void() plat_crush = 111 | { 112 | //dprint ("plat_crush\n"); 113 | 114 | other.deathtype = "squish"; // 1998-07-24 Wrong obituary messages fix by Zoid 115 | T_Damage (other, self, self, 1); 116 | 117 | if (self.state == STATE_UP) 118 | plat_go_down (); 119 | else if (self.state == STATE_DOWN) 120 | plat_go_up (); 121 | else 122 | objerror ("plat_crush: bad self.state\n"); 123 | }; 124 | 125 | void() plat_use = 126 | { 127 | self.use = SUB_Null; 128 | if (self.state != STATE_UP) 129 | objerror ("plat_use: not in up state"); 130 | plat_go_down(); 131 | }; 132 | 133 | 134 | /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER 135 | speed default 150 136 | 137 | Plats are always drawn in the extended position, so they will light correctly. 138 | 139 | If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat. 140 | 141 | If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height. 142 | Set "sounds" to one of the following: 143 | 1) base fast 144 | 2) chain slow 145 | */ 146 | 147 | 148 | void() func_plat = 149 | 150 | { 151 | 152 | if (!self.t_length) 153 | self.t_length = 80; 154 | if (!self.t_width) 155 | self.t_width = 10; 156 | 157 | if (self.sounds == 0) 158 | self.sounds = 2; 159 | // FIX THIS TO LOAD A GENERIC PLAT SOUND 160 | 161 | if (self.sounds == 1) 162 | { 163 | precache_sound ("plats/plat1.wav"); 164 | precache_sound ("plats/plat2.wav"); 165 | self.noise = "plats/plat1.wav"; 166 | self.noise1 = "plats/plat2.wav"; 167 | } 168 | 169 | if (self.sounds == 2) 170 | { 171 | precache_sound ("plats/medplat1.wav"); 172 | precache_sound ("plats/medplat2.wav"); 173 | self.noise = "plats/medplat1.wav"; 174 | self.noise1 = "plats/medplat2.wav"; 175 | } 176 | 177 | 178 | self.mangle = self.angles; 179 | self.angles = '0 0 0'; 180 | 181 | self.classname = "plat"; 182 | self.solid = SOLID_BSP; 183 | self.movetype = MOVETYPE_PUSH; 184 | setorigin (self, self.origin); 185 | setmodel (self, self.model); 186 | setsize (self, self.mins , self.maxs); 187 | 188 | self.blocked = plat_crush; 189 | if (!self.speed) 190 | self.speed = 150; 191 | 192 | // pos1 is the top position, pos2 is the bottom 193 | self.pos1 = self.origin; 194 | self.pos2 = self.origin; 195 | if (self.height) 196 | self.pos2_z = self.origin_z - self.height; 197 | else 198 | self.pos2_z = self.origin_z - self.size_z + 8; 199 | 200 | self.use = plat_trigger_use; 201 | 202 | plat_spawn_inside_trigger (); // the "start moving" trigger 203 | 204 | if (self.targetname) 205 | { 206 | self.state = STATE_UP; 207 | self.use = plat_use; 208 | } 209 | else 210 | { 211 | setorigin (self, self.pos2); 212 | self.state = STATE_BOTTOM; 213 | } 214 | }; 215 | 216 | //============================================================================ 217 | 218 | void() train_next; 219 | void() func_train_find; 220 | 221 | void() train_blocked = 222 | { 223 | if (time < self.attack_finished) 224 | return; 225 | self.attack_finished = time + 0.5; 226 | other.deathtype = "squish"; // 1998-07-24 Wrong obituary messages fix by Zoid 227 | T_Damage (other, self, self, self.dmg); 228 | }; 229 | void() train_use = 230 | { 231 | if (self.think != func_train_find) 232 | return; // already activated 233 | train_next(); 234 | }; 235 | 236 | void() train_wait = 237 | { 238 | if (self.wait) 239 | { 240 | self.nextthink = self.ltime + self.wait; 241 | sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); 242 | } 243 | else 244 | self.nextthink = self.ltime + 0.1; 245 | 246 | self.think = train_next; 247 | }; 248 | 249 | void() train_next = 250 | { 251 | local entity targ; 252 | 253 | targ = find (world, targetname, self.target); 254 | self.target = targ.target; 255 | if (!self.target) 256 | objerror ("train_next: no next target"); 257 | if (targ.wait) 258 | self.wait = targ.wait; 259 | else 260 | self.wait = 0; 261 | sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); 262 | SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait); 263 | }; 264 | 265 | void() func_train_find = 266 | 267 | { 268 | local entity targ; 269 | 270 | targ = find (world, targetname, self.target); 271 | self.target = targ.target; 272 | setorigin (self, targ.origin - self.mins); 273 | if (!self.targetname) 274 | { // not triggered, so start immediately 275 | self.nextthink = self.ltime + 0.1; 276 | self.think = train_next; 277 | } 278 | }; 279 | 280 | /*QUAKED func_train (0 .5 .8) ? 281 | Trains are moving platforms that players can ride. 282 | The targets origin specifies the min point of the train at each corner. 283 | The train spawns at the first target it is pointing at. 284 | If the train is the target of a button or trigger, it will not begin moving until activated. 285 | speed default 100 286 | dmg default 2 287 | sounds 288 | 1) ratchet metal 289 | 290 | */ 291 | void() func_train = 292 | { 293 | if (!self.speed) 294 | self.speed = 100; 295 | if (!self.target) 296 | objerror ("func_train without a target"); 297 | if (!self.dmg) 298 | self.dmg = 2; 299 | 300 | if (self.sounds == 0) 301 | { 302 | self.noise = ("misc/null.wav"); 303 | precache_sound ("misc/null.wav"); 304 | self.noise1 = ("misc/null.wav"); 305 | precache_sound ("misc/null.wav"); 306 | } 307 | 308 | if (self.sounds == 1) 309 | { 310 | self.noise = ("plats/train2.wav"); 311 | precache_sound ("plats/train2.wav"); 312 | self.noise1 = ("plats/train1.wav"); 313 | precache_sound ("plats/train1.wav"); 314 | } 315 | 316 | self.cnt = 1; 317 | self.solid = SOLID_BSP; 318 | self.movetype = MOVETYPE_PUSH; 319 | self.blocked = train_blocked; 320 | self.use = train_use; 321 | self.classname = "train"; 322 | 323 | setmodel (self, self.model); 324 | setsize (self, self.mins , self.maxs); 325 | setorigin (self, self.origin); 326 | 327 | // start trains on the second frame, to make sure their targets have had 328 | // a chance to spawn 329 | self.nextthink = self.ltime + 0.1; 330 | self.think = func_train_find; 331 | }; 332 | 333 | /*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8) 334 | This is used for the final bos 335 | */ 336 | void() misc_teleporttrain = 337 | { 338 | if (!self.speed) 339 | self.speed = 100; 340 | if (!self.target) 341 | objerror ("func_train without a target"); 342 | 343 | self.cnt = 1; 344 | self.solid = SOLID_NOT; 345 | self.movetype = MOVETYPE_PUSH; 346 | self.blocked = train_blocked; 347 | self.use = train_use; 348 | self.avelocity = '100 200 300'; 349 | 350 | self.noise = ("misc/null.wav"); 351 | precache_sound ("misc/null.wav"); 352 | self.noise1 = ("misc/null.wav"); 353 | precache_sound ("misc/null.wav"); 354 | 355 | precache_model2 ("progs/teleport.mdl"); 356 | setmodel (self, "progs/teleport.mdl"); 357 | setsize (self, self.mins , self.maxs); 358 | setorigin (self, self.origin); 359 | 360 | // start trains on the second frame, to make sure their targets have had 361 | // a chance to spawn 362 | self.nextthink = self.ltime + 0.1; 363 | self.think = func_train_find; 364 | }; 365 | -------------------------------------------------------------------------------- /src/progs.src: -------------------------------------------------------------------------------- 1 | ../progs.dat 2 | 3 | defs.qc 4 | subs.qc 5 | fight.qc 6 | ai.qc 7 | combat.qc 8 | items.qc 9 | weapons.qc 10 | world.qc 11 | client.qc 12 | player.qc 13 | monsters.qc 14 | doors.qc 15 | buttons.qc 16 | triggers.qc 17 | plats.qc 18 | misc.qc 19 | 20 | ogre.qc 21 | demon.qc 22 | shambler.qc 23 | knight.qc 24 | soldier.qc 25 | wizard.qc 26 | dog.qc 27 | zombie.qc 28 | boss.qc 29 | 30 | spawn.qc // registered -- this has been renamed, see line 188 for info -- dumptruck_ds 31 | hknight.qc // registered 32 | fish.qc // registered 33 | shalrath.qc // registered 34 | enforcer.qc // registered 35 | oldone.qc // registered 36 | -------------------------------------------------------------------------------- /src/shalrath.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | SHAL-RATH 5 | 6 | ============================================================================== 7 | */ 8 | $cd id1/models/shalrath 9 | $origin 0 0 24 10 | $base base 11 | $skin skin 12 | $scale 0.7 13 | 14 | $frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8 15 | $frame attack9 attack10 attack11 16 | 17 | $frame pain1 pain2 pain3 pain4 pain5 18 | 19 | $frame death1 death2 death3 death4 death5 death6 death7 20 | 21 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 22 | $frame walk11 walk12 23 | 24 | void(entity attacker, float damage) shalrath_pain; // compiler warning fixes from mod_jam_progs -- dumptruck_ds 25 | void() ShalMissile; 26 | void() shal_stand =[ $walk1, shal_stand ] {ai_stand();}; 27 | 28 | void() shal_walk1 =[ $walk2, shal_walk2 ] { 29 | if (random() < 0.2) 30 | sound (self, CHAN_VOICE, "shalrath/idle.wav", 1, ATTN_IDLE); 31 | ai_walk(6);}; 32 | void() shal_walk2 =[ $walk3, shal_walk3 ] {ai_walk(4);}; 33 | void() shal_walk3 =[ $walk4, shal_walk4 ] {ai_walk(0);}; 34 | void() shal_walk4 =[ $walk5, shal_walk5 ] {ai_walk(0);}; 35 | void() shal_walk5 =[ $walk6, shal_walk6 ] {ai_walk(0);}; 36 | void() shal_walk6 =[ $walk7, shal_walk7 ] {ai_walk(0);}; 37 | void() shal_walk7 =[ $walk8, shal_walk8 ] {ai_walk(5);}; 38 | void() shal_walk8 =[ $walk9, shal_walk9 ] {ai_walk(6);}; 39 | void() shal_walk9 =[ $walk10, shal_walk10 ] {ai_walk(5);}; 40 | void() shal_walk10 =[ $walk11, shal_walk11 ] {ai_walk(0);}; 41 | void() shal_walk11 =[ $walk12, shal_walk12 ] {ai_walk(4);}; 42 | void() shal_walk12 =[ $walk1, shal_walk1 ] {ai_walk(5);}; 43 | 44 | void() shal_run1 =[ $walk2, shal_run2 ] { 45 | if (random() < 0.2) 46 | sound (self, CHAN_VOICE, "shalrath/idle.wav", 1, ATTN_IDLE); 47 | ai_run(6);}; 48 | void() shal_run2 =[ $walk3, shal_run3 ] {ai_run(4);}; 49 | void() shal_run3 =[ $walk4, shal_run4 ] {ai_run(0);}; 50 | void() shal_run4 =[ $walk5, shal_run5 ] {ai_run(0);}; 51 | void() shal_run5 =[ $walk6, shal_run6 ] {ai_run(0);}; 52 | void() shal_run6 =[ $walk7, shal_run7 ] {ai_run(0);}; 53 | void() shal_run7 =[ $walk8, shal_run8 ] {ai_run(5);}; 54 | void() shal_run8 =[ $walk9, shal_run9 ] {ai_run(6);}; 55 | void() shal_run9 =[ $walk10, shal_run10 ] {ai_run(5);}; 56 | void() shal_run10 =[ $walk11, shal_run11 ] {ai_run(0);}; 57 | void() shal_run11 =[ $walk12, shal_run12 ] {ai_run(4);}; 58 | void() shal_run12 =[ $walk1, shal_run1 ] {ai_run(5);}; 59 | 60 | void() shal_attack1 =[ $attack1, shal_attack2 ] { 61 | sound (self, CHAN_VOICE, "shalrath/attack.wav", 1, ATTN_NORM); 62 | ai_face(); 63 | }; 64 | void() shal_attack2 =[ $attack2, shal_attack3 ] {ai_face();}; 65 | void() shal_attack3 =[ $attack3, shal_attack4 ] {ai_face();}; 66 | void() shal_attack4 =[ $attack4, shal_attack5 ] {ai_face();}; 67 | void() shal_attack5 =[ $attack5, shal_attack6 ] {ai_face();}; 68 | void() shal_attack6 =[ $attack6, shal_attack7 ] {ai_face();}; 69 | void() shal_attack7 =[ $attack7, shal_attack8 ] {ai_face();}; 70 | void() shal_attack8 =[ $attack8, shal_attack9 ] {ai_face();}; 71 | void() shal_attack9 =[ $attack9, shal_attack10 ] {ShalMissile();}; 72 | void() shal_attack10 =[ $attack10, shal_attack11 ] {ai_face();}; 73 | void() shal_attack11 =[ $attack11, shal_run1 ] {}; 74 | 75 | void() shal_pain1 =[ $pain1, shal_pain2 ] {}; 76 | void() shal_pain2 =[ $pain2, shal_pain3 ] {}; 77 | void() shal_pain3 =[ $pain3, shal_pain4 ] {}; 78 | void() shal_pain4 =[ $pain4, shal_pain5 ] {}; 79 | void() shal_pain5 =[ $pain5, shal_run1 ] {}; 80 | 81 | void() shal_death1 =[ $death1, shal_death2 ] {}; 82 | void() shal_death2 =[ $death2, shal_death3 ] {}; 83 | void() shal_death3 =[ $death3, shal_death4 ] {}; 84 | void() shal_death4 =[ $death4, shal_death5 ] {}; 85 | void() shal_death5 =[ $death5, shal_death6 ] {}; 86 | void() shal_death6 =[ $death6, shal_death7 ] {}; 87 | void() shal_death7 =[ $death7, shal_death7 ] {}; 88 | 89 | 90 | void(entity attacker, float damage) shalrath_pain = // compiler warning fixes from mod_jam_progs -- dumptruck_ds 91 | { 92 | if (self.pain_finished > time) 93 | return; 94 | 95 | sound (self, CHAN_VOICE, "shalrath/pain.wav", 1, ATTN_NORM); 96 | shal_pain1(); 97 | self.pain_finished = time + 3; 98 | }; 99 | 100 | void() shalrath_die = 101 | { 102 | // check for gib 103 | if (self.health < -90) 104 | { 105 | ThrowHead ("progs/h_shal.mdl", self.health); 106 | ThrowGib ("progs/gib1.mdl", self.health); 107 | ThrowGib ("progs/gib2.mdl", self.health); 108 | ThrowGib ("progs/gib3.mdl", self.health); 109 | // spinstagib sounds 110 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 111 | { 112 | GibSoundsRandom(); 113 | return; 114 | } 115 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 116 | return; 117 | } 118 | 119 | sound (self, CHAN_VOICE, "shalrath/death.wav", 1, ATTN_NORM); 120 | shal_death1(); 121 | self.solid = SOLID_NOT; 122 | // insert death sounds here 123 | }; 124 | 125 | /* 126 | ================ 127 | ShalMissile 128 | ================ 129 | */ 130 | void() ShalMissileTouch; 131 | void() ShalHome; 132 | void() ShalMissile = 133 | { 134 | local entity missile; 135 | local vector dir; 136 | local float dist, flytime; 137 | 138 | dir = normalize((self.enemy.origin + '0 0 10') - self.origin); 139 | dist = vlen (self.enemy.origin - self.origin); 140 | flytime = dist * 0.002; 141 | if (flytime < 0.1) 142 | flytime = 0.1; 143 | 144 | self.effects = self.effects | EF_MUZZLEFLASH; 145 | sound (self, CHAN_WEAPON, "shalrath/attack2.wav", 1, ATTN_NORM); 146 | 147 | missile = spawn (); 148 | missile.owner = self; 149 | 150 | missile.solid = SOLID_BBOX; 151 | missile.movetype = MOVETYPE_FLYMISSILE; 152 | setmodel (missile, "progs/v_spike.mdl"); 153 | 154 | setsize (missile, '0 0 0', '0 0 0'); 155 | 156 | missile.origin = self.origin + '0 0 10'; 157 | missile.velocity = dir * 400; 158 | missile.avelocity = '300 300 300'; 159 | missile.nextthink = flytime + time; 160 | missile.think = ShalHome; 161 | missile.enemy = self.enemy; 162 | missile.touch = ShalMissileTouch; 163 | }; 164 | 165 | void() ShalHome = 166 | { 167 | local vector dir, vtemp; 168 | vtemp = self.enemy.origin + '0 0 10'; 169 | if (self.enemy.health < 1) 170 | { 171 | remove(self); 172 | return; 173 | } 174 | dir = normalize(vtemp - self.origin); 175 | if (skill == 3) 176 | self.velocity = dir * 350; 177 | else 178 | self.velocity = dir * 250; 179 | self.nextthink = time + 0.2; 180 | self.think = ShalHome; 181 | }; 182 | 183 | void() ShalMissileTouch = 184 | { 185 | if (other == self.owner) 186 | return; // don't explode on owner 187 | 188 | if (other.classname == "monster_zombie") 189 | T_Damage (other, self, self, 110); 190 | T_RadiusDamage (self, self.owner, 40, world, ""); // 1998-07-24 Wrong obituary messages fix by Zoid 191 | sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM); 192 | 193 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 194 | WriteByte (MSG_BROADCAST, TE_EXPLOSION); 195 | WriteCoord (MSG_BROADCAST, self.origin_x); 196 | WriteCoord (MSG_BROADCAST, self.origin_y); 197 | WriteCoord (MSG_BROADCAST, self.origin_z); 198 | 199 | self.velocity = '0 0 0'; 200 | self.touch = SUB_Null; 201 | setmodel (self, "progs/s_explod.spr"); 202 | self.solid = SOLID_NOT; 203 | s_explode1 (); 204 | }; 205 | 206 | //================================================================= 207 | 208 | /*QUAKED monster_shalrath (1 0 0) (-32 -32 -24) (32 32 48) Ambush 209 | */ 210 | void() monster_shalrath = 211 | { 212 | if (deathmatch) 213 | { 214 | remove(self); 215 | return; 216 | } 217 | precache_model2 ("progs/shalrath.mdl"); 218 | precache_model2 ("progs/h_shal.mdl"); 219 | precache_model2 ("progs/v_spike.mdl"); 220 | 221 | precache_sound2 ("shalrath/attack.wav"); 222 | precache_sound2 ("shalrath/attack2.wav"); 223 | precache_sound2 ("shalrath/death.wav"); 224 | precache_sound2 ("shalrath/idle.wav"); 225 | precache_sound2 ("shalrath/pain.wav"); 226 | precache_sound2 ("shalrath/sight.wav"); 227 | 228 | self.solid = SOLID_SLIDEBOX; 229 | self.movetype = MOVETYPE_STEP; 230 | 231 | setmodel (self, "progs/shalrath.mdl"); 232 | setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); 233 | self.health = 400; 234 | 235 | self.th_stand = shal_stand; 236 | self.th_walk = shal_walk1; 237 | self.th_run = shal_run1; 238 | self.th_die = shalrath_die; 239 | self.th_pain = shalrath_pain; 240 | self.th_missile = shal_attack1; 241 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 242 | self.touch = monster_touch; 243 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 244 | 245 | self.think = walkmonster_start; 246 | self.nextthink = time + 0.1 + random ()*0.1; 247 | 248 | }; 249 | -------------------------------------------------------------------------------- /src/soldier.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | SOLDIER / PLAYER 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/soldier3 10 | $origin 0 -6 24 11 | $base base 12 | $skin skin 13 | 14 | $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 15 | 16 | $frame death1 death2 death3 death4 death5 death6 death7 death8 17 | $frame death9 death10 18 | 19 | $frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8 20 | $frame deathc9 deathc10 deathc11 21 | 22 | $frame load1 load2 load3 load4 load5 load6 load7 load8 load9 load10 load11 23 | 24 | $frame pain1 pain2 pain3 pain4 pain5 pain6 25 | 26 | $frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10 27 | $frame painb11 painb12 painb13 painb14 28 | 29 | $frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8 painc9 painc10 30 | $frame painc11 painc12 painc13 31 | 32 | $frame run1 run2 run3 run4 run5 run6 run7 run8 33 | 34 | $frame shoot1 shoot2 shoot3 shoot4 shoot5 shoot6 shoot7 shoot8 shoot9 35 | 36 | $frame prowl_1 prowl_2 prowl_3 prowl_4 prowl_5 prowl_6 prowl_7 prowl_8 37 | $frame prowl_9 prowl_10 prowl_11 prowl_12 prowl_13 prowl_14 prowl_15 prowl_16 38 | $frame prowl_17 prowl_18 prowl_19 prowl_20 prowl_21 prowl_22 prowl_23 prowl_24 39 | 40 | /* 41 | ============================================================================== 42 | SOLDIER CODE 43 | ============================================================================== 44 | */ 45 | 46 | void() army_fire; 47 | 48 | void() army_stand1 =[ $stand1, army_stand2 ] {ai_stand();}; 49 | void() army_stand2 =[ $stand2, army_stand3 ] {ai_stand();}; 50 | void() army_stand3 =[ $stand3, army_stand4 ] {ai_stand();}; 51 | void() army_stand4 =[ $stand4, army_stand5 ] {ai_stand();}; 52 | void() army_stand5 =[ $stand5, army_stand6 ] {ai_stand();}; 53 | void() army_stand6 =[ $stand6, army_stand7 ] {ai_stand();}; 54 | void() army_stand7 =[ $stand7, army_stand8 ] {ai_stand();}; 55 | void() army_stand8 =[ $stand8, army_stand1 ] {ai_stand();}; 56 | 57 | void() army_walk1 =[ $prowl_1, army_walk2 ] { 58 | if (random() < 0.2) 59 | sound (self, CHAN_VOICE, "soldier/idle.wav", 1, ATTN_IDLE); 60 | ai_walk(1);}; 61 | void() army_walk2 =[ $prowl_2, army_walk3 ] {ai_walk(1);}; 62 | void() army_walk3 =[ $prowl_3, army_walk4 ] {ai_walk(1);}; 63 | void() army_walk4 =[ $prowl_4, army_walk5 ] {ai_walk(1);}; 64 | void() army_walk5 =[ $prowl_5, army_walk6 ] {ai_walk(2);}; 65 | void() army_walk6 =[ $prowl_6, army_walk7 ] {ai_walk(3);}; 66 | void() army_walk7 =[ $prowl_7, army_walk8 ] {ai_walk(4);}; 67 | void() army_walk8 =[ $prowl_8, army_walk9 ] {ai_walk(4);}; 68 | void() army_walk9 =[ $prowl_9, army_walk10 ] {ai_walk(2);}; 69 | void() army_walk10 =[ $prowl_10, army_walk11 ] {ai_walk(2);}; 70 | void() army_walk11 =[ $prowl_11, army_walk12 ] {ai_walk(2);}; 71 | void() army_walk12 =[ $prowl_12, army_walk13 ] {ai_walk(1);}; 72 | void() army_walk13 =[ $prowl_13, army_walk14 ] {ai_walk(0);}; 73 | void() army_walk14 =[ $prowl_14, army_walk15 ] {ai_walk(1);}; 74 | void() army_walk15 =[ $prowl_15, army_walk16 ] {ai_walk(1);}; 75 | void() army_walk16 =[ $prowl_16, army_walk17 ] {ai_walk(1);}; 76 | void() army_walk17 =[ $prowl_17, army_walk18 ] {ai_walk(3);}; 77 | void() army_walk18 =[ $prowl_18, army_walk19 ] {ai_walk(3);}; 78 | void() army_walk19 =[ $prowl_19, army_walk20 ] {ai_walk(3);}; 79 | void() army_walk20 =[ $prowl_20, army_walk21 ] {ai_walk(3);}; 80 | void() army_walk21 =[ $prowl_21, army_walk22 ] {ai_walk(2);}; 81 | void() army_walk22 =[ $prowl_22, army_walk23 ] {ai_walk(1);}; 82 | void() army_walk23 =[ $prowl_23, army_walk24 ] {ai_walk(1);}; 83 | void() army_walk24 =[ $prowl_24, army_walk1 ] {ai_walk(1);}; 84 | 85 | void() army_run1 =[ $run1, army_run2 ] { 86 | if (random() < 0.2) 87 | sound (self, CHAN_VOICE, "soldier/idle.wav", 1, ATTN_IDLE); 88 | ai_run(11);}; 89 | void() army_run2 =[ $run2, army_run3 ] {ai_run(15);}; 90 | void() army_run3 =[ $run3, army_run4 ] {ai_run(10);}; 91 | void() army_run4 =[ $run4, army_run5 ] {ai_run(10);}; 92 | void() army_run5 =[ $run5, army_run6 ] {ai_run(8);}; 93 | void() army_run6 =[ $run6, army_run7 ] {ai_run(15);}; 94 | void() army_run7 =[ $run7, army_run8 ] {ai_run(10);}; 95 | void() army_run8 =[ $run8, army_run1 ] {ai_run(8);}; 96 | 97 | void() army_atk1 =[ $shoot1, army_atk2 ] {ai_face();}; 98 | void() army_atk2 =[ $shoot2, army_atk3 ] {ai_face();}; 99 | void() army_atk3 =[ $shoot3, army_atk4 ] {ai_face();}; 100 | void() army_atk4 =[ $shoot4, army_atk5 ] {ai_face();}; 101 | void() army_atk5 =[ $shoot5, army_atk6 ] {ai_face();army_fire(); 102 | self.effects = self.effects | EF_MUZZLEFLASH;}; 103 | void() army_atk6 =[ $shoot6, army_atk7 ] {ai_face();}; 104 | void() army_atk7 =[ $shoot7, army_atk8 ] {ai_face();SUB_CheckRefire (army_atk1);}; 105 | void() army_atk8 =[ $shoot8, army_atk9 ] {ai_face();}; 106 | void() army_atk9 =[ $shoot9, army_run1 ] {ai_face();}; 107 | 108 | 109 | void() army_pain1 =[ $pain1, army_pain2 ] {}; 110 | void() army_pain2 =[ $pain2, army_pain3 ] {}; 111 | void() army_pain3 =[ $pain3, army_pain4 ] {}; 112 | void() army_pain4 =[ $pain4, army_pain5 ] {}; 113 | void() army_pain5 =[ $pain5, army_pain6 ] {}; 114 | void() army_pain6 =[ $pain6, army_run1 ] {ai_pain(1);}; 115 | 116 | void() army_painb1 =[ $painb1, army_painb2 ] {}; 117 | void() army_painb2 =[ $painb2, army_painb3 ] {ai_painforward(13);}; 118 | void() army_painb3 =[ $painb3, army_painb4 ] {ai_painforward(9);}; 119 | void() army_painb4 =[ $painb4, army_painb5 ] {}; 120 | void() army_painb5 =[ $painb5, army_painb6 ] {}; 121 | void() army_painb6 =[ $painb6, army_painb7 ] {}; 122 | void() army_painb7 =[ $painb7, army_painb8 ] {}; 123 | void() army_painb8 =[ $painb8, army_painb9 ] {}; 124 | void() army_painb9 =[ $painb9, army_painb10] {}; 125 | void() army_painb10=[ $painb10, army_painb11] {}; 126 | void() army_painb11=[ $painb11, army_painb12] {}; 127 | void() army_painb12=[ $painb12, army_painb13] {ai_pain(2);}; 128 | void() army_painb13=[ $painb13, army_painb14] {}; 129 | void() army_painb14=[ $painb14, army_run1 ] {}; 130 | 131 | void() army_painc1 =[ $painc1, army_painc2 ] {}; 132 | void() army_painc2 =[ $painc2, army_painc3 ] {ai_pain(1);}; 133 | void() army_painc3 =[ $painc3, army_painc4 ] {}; 134 | void() army_painc4 =[ $painc4, army_painc5 ] {}; 135 | void() army_painc5 =[ $painc5, army_painc6 ] {ai_painforward(1);}; 136 | void() army_painc6 =[ $painc6, army_painc7 ] {ai_painforward(1);}; 137 | void() army_painc7 =[ $painc7, army_painc8 ] {}; 138 | void() army_painc8 =[ $painc8, army_painc9 ] {ai_pain(1);}; 139 | void() army_painc9 =[ $painc9, army_painc10] {ai_painforward(4);}; 140 | void() army_painc10=[ $painc10, army_painc11] {ai_painforward(3);}; 141 | void() army_painc11=[ $painc11, army_painc12] {ai_painforward(6);}; 142 | void() army_painc12=[ $painc12, army_painc13] {ai_painforward(8);}; 143 | void() army_painc13=[ $painc13, army_run1] {}; 144 | 145 | void(entity attacker, float damage) army_pain = 146 | { 147 | local float r; 148 | 149 | if (self.pain_finished > time) 150 | return; 151 | 152 | r = random(); 153 | 154 | if (r < 0.2) 155 | { 156 | self.pain_finished = time + 0.6; 157 | army_pain1 (); 158 | sound (self, CHAN_VOICE, "soldier/pain1.wav", 1, ATTN_NORM); 159 | } 160 | else if (r < 0.6) 161 | { 162 | self.pain_finished = time + 1.1; 163 | army_painb1 (); 164 | sound (self, CHAN_VOICE, "soldier/pain2.wav", 1, ATTN_NORM); 165 | } 166 | else 167 | { 168 | self.pain_finished = time + 1.1; 169 | army_painc1 (); 170 | sound (self, CHAN_VOICE, "soldier/pain2.wav", 1, ATTN_NORM); 171 | } 172 | }; 173 | 174 | 175 | void() army_fire = 176 | { 177 | local vector dir; 178 | local entity en; 179 | 180 | ai_face(); 181 | 182 | sound (self, CHAN_WEAPON, "soldier/sattck1.wav", 1, ATTN_NORM); 183 | 184 | // fire somewhat behind the player, so a dodging player is harder to hit 185 | en = self.enemy; 186 | 187 | dir = en.origin - en.velocity*0.2; 188 | dir = normalize (dir - self.origin); 189 | 190 | FireBullets (4, dir, '0.1 0.1 0'); 191 | }; 192 | 193 | 194 | 195 | void() army_die1 =[ $death1, army_die2 ] {}; 196 | void() army_die2 =[ $death2, army_die3 ] {}; 197 | void() army_die3 =[ $death3, army_die4 ] 198 | {self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();}; 199 | void() army_die4 =[ $death4, army_die5 ] {}; 200 | void() army_die5 =[ $death5, army_die6 ] {}; 201 | void() army_die6 =[ $death6, army_die7 ] {}; 202 | void() army_die7 =[ $death7, army_die8 ] {}; 203 | void() army_die8 =[ $death8, army_die9 ] {}; 204 | void() army_die9 =[ $death9, army_die10 ] {}; 205 | void() army_die10 =[ $death10, army_die10 ] {}; 206 | 207 | void() army_cdie1 =[ $deathc1, army_cdie2 ] {}; 208 | void() army_cdie2 =[ $deathc2, army_cdie3 ] {ai_back(5);}; 209 | void() army_cdie3 =[ $deathc3, army_cdie4 ] 210 | {self.solid = SOLID_NOT;self.ammo_shells = 5;DropBackpack();ai_back(4);}; 211 | void() army_cdie4 =[ $deathc4, army_cdie5 ] {ai_back(13);}; 212 | void() army_cdie5 =[ $deathc5, army_cdie6 ] {ai_back(3);}; 213 | void() army_cdie6 =[ $deathc6, army_cdie7 ] {ai_back(4);}; 214 | void() army_cdie7 =[ $deathc7, army_cdie8 ] {}; 215 | void() army_cdie8 =[ $deathc8, army_cdie9 ] {}; 216 | void() army_cdie9 =[ $deathc9, army_cdie10 ] {}; 217 | void() army_cdie10 =[ $deathc10, army_cdie11 ] {}; 218 | void() army_cdie11 =[ $deathc11, army_cdie11 ] {}; 219 | 220 | 221 | void() army_die = 222 | { 223 | // check for gib 224 | if (self.health < -35) 225 | { 226 | ThrowHead ("progs/h_guard.mdl", self.health); 227 | ThrowGib ("progs/gib1.mdl", self.health); 228 | ThrowGib ("progs/gib2.mdl", self.health); 229 | ThrowGib ("progs/gib3.mdl", self.health); 230 | // spinstagib sounds 231 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 232 | { 233 | GibSoundsRandom(); 234 | return; 235 | } 236 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 237 | return; 238 | } 239 | 240 | // regular death 241 | sound (self, CHAN_VOICE, "soldier/death1.wav", 1, ATTN_NORM); 242 | if (random() < 0.5) 243 | army_die1 (); 244 | else 245 | army_cdie1 (); 246 | }; 247 | 248 | 249 | /*QUAKED monster_army (1 0 0) (-16 -16 -24) (16 16 40) Ambush 250 | */ 251 | void() monster_army = 252 | { 253 | if (deathmatch) 254 | { 255 | remove(self); 256 | return; 257 | } 258 | precache_model ("progs/soldier.mdl"); 259 | precache_model ("progs/h_guard.mdl"); 260 | precache_model ("progs/gib1.mdl"); 261 | precache_model ("progs/gib2.mdl"); 262 | precache_model ("progs/gib3.mdl"); 263 | 264 | precache_sound ("soldier/death1.wav"); 265 | precache_sound ("soldier/idle.wav"); 266 | precache_sound ("soldier/pain1.wav"); 267 | precache_sound ("soldier/pain2.wav"); 268 | precache_sound ("soldier/sattck1.wav"); 269 | precache_sound ("soldier/sight1.wav"); 270 | 271 | precache_sound ("player/udeath.wav"); // gib death 272 | 273 | 274 | self.solid = SOLID_SLIDEBOX; 275 | self.movetype = MOVETYPE_STEP; 276 | 277 | setmodel (self, "progs/soldier.mdl"); 278 | 279 | setsize (self, '-16 -16 -24', '16 16 40'); 280 | self.health = 30; 281 | 282 | self.th_stand = army_stand1; 283 | self.th_walk = army_walk1; 284 | self.th_run = army_run1; 285 | self.th_missile = army_atk1; 286 | self.th_pain = army_pain; 287 | self.th_die = army_die; 288 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 289 | self.touch = monster_touch; 290 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 291 | 292 | walkmonster_start (); 293 | }; 294 | -------------------------------------------------------------------------------- /src/spawn.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | BLOB 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/tarbaby 10 | $origin 0 0 24 11 | $base base 12 | 13 | $skin skin 14 | 15 | $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 16 | $frame walk11 walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19 17 | $frame walk20 walk21 walk22 walk23 walk24 walk25 18 | 19 | $frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12 run13 20 | $frame run14 run15 run16 run17 run18 run19 run20 run21 run22 run23 21 | $frame run24 run25 22 | 23 | $frame jump1 jump2 jump3 jump4 jump5 jump6 24 | 25 | $frame fly1 fly2 fly3 fly4 26 | 27 | $frame exp 28 | 29 | void() tbaby_stand1 =[ $walk1, tbaby_stand1 ] {ai_stand();}; 30 | 31 | void() tbaby_hang1 =[ $walk1, tbaby_hang1 ] {ai_stand();}; 32 | 33 | void() tbaby_walk1 =[ $walk1, tbaby_walk2 ] {ai_turn();}; 34 | void() tbaby_walk2 =[ $walk2, tbaby_walk3 ] {ai_turn();}; 35 | void() tbaby_walk3 =[ $walk3, tbaby_walk4 ] {ai_turn();}; 36 | void() tbaby_walk4 =[ $walk4, tbaby_walk5 ] {ai_turn();}; 37 | void() tbaby_walk5 =[ $walk5, tbaby_walk6 ] {ai_turn();}; 38 | void() tbaby_walk6 =[ $walk6, tbaby_walk7 ] {ai_turn();}; 39 | void() tbaby_walk7 =[ $walk7, tbaby_walk8 ] {ai_turn();}; 40 | void() tbaby_walk8 =[ $walk8, tbaby_walk9 ] {ai_turn();}; 41 | void() tbaby_walk9 =[ $walk9, tbaby_walk10 ] {ai_turn();}; 42 | void() tbaby_walk10 =[ $walk10, tbaby_walk11 ] {ai_turn();}; 43 | void() tbaby_walk11 =[ $walk11, tbaby_walk12 ] {ai_walk(2);}; 44 | void() tbaby_walk12 =[ $walk12, tbaby_walk13 ] {ai_walk(2);}; 45 | void() tbaby_walk13 =[ $walk13, tbaby_walk14 ] {ai_walk(2);}; 46 | void() tbaby_walk14 =[ $walk14, tbaby_walk15 ] {ai_walk(2);}; 47 | void() tbaby_walk15 =[ $walk15, tbaby_walk16 ] {ai_walk(2);}; 48 | void() tbaby_walk16 =[ $walk16, tbaby_walk17 ] {ai_walk(2);}; 49 | void() tbaby_walk17 =[ $walk17, tbaby_walk18 ] {ai_walk(2);}; 50 | void() tbaby_walk18 =[ $walk18, tbaby_walk19 ] {ai_walk(2);}; 51 | void() tbaby_walk19 =[ $walk19, tbaby_walk20 ] {ai_walk(2);}; 52 | void() tbaby_walk20 =[ $walk20, tbaby_walk21 ] {ai_walk(2);}; 53 | void() tbaby_walk21 =[ $walk21, tbaby_walk22 ] {ai_walk(2);}; 54 | void() tbaby_walk22 =[ $walk22, tbaby_walk23 ] {ai_walk(2);}; 55 | void() tbaby_walk23 =[ $walk23, tbaby_walk24 ] {ai_walk(2);}; 56 | void() tbaby_walk24 =[ $walk24, tbaby_walk25 ] {ai_walk(2);}; 57 | void() tbaby_walk25 =[ $walk25, tbaby_walk1 ] {ai_walk(2);}; 58 | 59 | void() tbaby_run1 =[ $run1, tbaby_run2 ] {ai_face();}; 60 | void() tbaby_run2 =[ $run2, tbaby_run3 ] {ai_face();}; 61 | void() tbaby_run3 =[ $run3, tbaby_run4 ] {ai_face();}; 62 | void() tbaby_run4 =[ $run4, tbaby_run5 ] {ai_face();}; 63 | void() tbaby_run5 =[ $run5, tbaby_run6 ] {ai_face();}; 64 | void() tbaby_run6 =[ $run6, tbaby_run7 ] {ai_face();}; 65 | void() tbaby_run7 =[ $run7, tbaby_run8 ] {ai_face();}; 66 | void() tbaby_run8 =[ $run8, tbaby_run9 ] {ai_face();}; 67 | void() tbaby_run9 =[ $run9, tbaby_run10 ] {ai_face();}; 68 | void() tbaby_run10 =[ $run10, tbaby_run11 ] {ai_face();}; 69 | void() tbaby_run11 =[ $run11, tbaby_run12 ] {ai_run(2);}; 70 | void() tbaby_run12 =[ $run12, tbaby_run13 ] {ai_run(2);}; 71 | void() tbaby_run13 =[ $run13, tbaby_run14 ] {ai_run(2);}; 72 | void() tbaby_run14 =[ $run14, tbaby_run15 ] {ai_run(2);}; 73 | void() tbaby_run15 =[ $run15, tbaby_run16 ] {ai_run(2);}; 74 | void() tbaby_run16 =[ $run16, tbaby_run17 ] {ai_run(2);}; 75 | void() tbaby_run17 =[ $run17, tbaby_run18 ] {ai_run(2);}; 76 | void() tbaby_run18 =[ $run18, tbaby_run19 ] {ai_run(2);}; 77 | void() tbaby_run19 =[ $run19, tbaby_run20 ] {ai_run(2);}; 78 | void() tbaby_run20 =[ $run20, tbaby_run21 ] {ai_run(2);}; 79 | void() tbaby_run21 =[ $run21, tbaby_run22 ] {ai_run(2);}; 80 | void() tbaby_run22 =[ $run22, tbaby_run23 ] {ai_run(2);}; 81 | void() tbaby_run23 =[ $run23, tbaby_run24 ] {ai_run(2);}; 82 | void() tbaby_run24 =[ $run24, tbaby_run25 ] {ai_run(2);}; 83 | void() tbaby_run25 =[ $run25, tbaby_run1 ] {ai_run(2);}; 84 | 85 | 86 | //============================================================================ 87 | 88 | 89 | void() tbaby_jump1; 90 | 91 | void() Tar_JumpTouch = 92 | { 93 | local float ldmg; 94 | 95 | if (other.takedamage && other.classname != self.classname) 96 | { 97 | if ( vlen(self.velocity) > 400 ) 98 | { 99 | ldmg = 10 + 10*random(); 100 | T_Damage (other, self, self, ldmg); 101 | sound (self, CHAN_WEAPON, "blob/hit1.wav", 1, ATTN_NORM); 102 | } 103 | } 104 | else 105 | sound (self, CHAN_WEAPON, "blob/land1.wav", 1, ATTN_NORM); 106 | 107 | 108 | if (!checkbottom(self)) 109 | { 110 | if (self.flags & FL_ONGROUND) 111 | { // jump randomly to not get hung up 112 | // dprint ("popjump\n"); 113 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 114 | // self.touch = SUB_Null; 115 | self.touch = monster_touch; 116 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 117 | self.think = tbaby_run1; 118 | self.movetype = MOVETYPE_STEP; 119 | self.nextthink = time + 0.1; 120 | 121 | // self.velocity_x = (random() - 0.5) * 600; 122 | // self.velocity_y = (random() - 0.5) * 600; 123 | // self.velocity_z = 200; 124 | // self.flags = self.flags - FL_ONGROUND; 125 | } 126 | return; // not on ground yet 127 | } 128 | 129 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 130 | // self.touch = SUB_Null; 131 | self.touch = monster_touch; 132 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 133 | self.think = tbaby_jump1; 134 | self.nextthink = time + 0.1; 135 | }; 136 | 137 | void() tbaby_jump5; 138 | 139 | void() tbaby_fly1 =[ $fly1, tbaby_fly2 ] {}; 140 | void() tbaby_fly2 =[ $fly2, tbaby_fly3 ] {}; 141 | void() tbaby_fly3 =[ $fly3, tbaby_fly4 ] {}; 142 | void() tbaby_fly4 =[ $fly4, tbaby_fly1 ] { 143 | self.cnt = self.cnt + 1; 144 | if (self.cnt == 4) 145 | { 146 | //dprint ("spawn hop\n"); 147 | tbaby_jump5 (); 148 | } 149 | }; 150 | 151 | void() tbaby_jump1 =[ $jump1, tbaby_jump2 ] {ai_face();}; 152 | void() tbaby_jump2 =[ $jump2, tbaby_jump3 ] {ai_face();}; 153 | void() tbaby_jump3 =[ $jump3, tbaby_jump4 ] {ai_face();}; 154 | void() tbaby_jump4 =[ $jump4, tbaby_jump5 ] {ai_face();}; 155 | void() tbaby_jump5 =[ $jump5, tbaby_jump6 ] 156 | { 157 | self.movetype = MOVETYPE_BOUNCE; 158 | self.touch = Tar_JumpTouch; 159 | makevectors (self.angles); 160 | self.origin_z = self.origin_z + 1; 161 | self.velocity = v_forward * 600 + '0 0 200'; 162 | self.velocity_z = self.velocity_z + random()*150; 163 | if (self.flags & FL_ONGROUND) 164 | self.flags = self.flags - FL_ONGROUND; 165 | self.cnt = 0; 166 | }; 167 | void() tbaby_jump6 =[ $jump6,tbaby_fly1 ] {}; 168 | 169 | 170 | 171 | //============================================================================= 172 | 173 | void() tbaby_die1 =[ $exp, tbaby_die2 ] { 174 | self.takedamage = DAMAGE_NO; 175 | }; 176 | void() tbaby_die2 =[ $exp, tbaby_run1 ] 177 | { 178 | T_RadiusDamage (self, self, 120, world, ""); // 1998-07-24 Wrong obituary messages fix by Zoid 179 | 180 | sound (self, CHAN_VOICE, "blob/death1.wav", 1, ATTN_NORM); 181 | self.origin = self.origin - 8*normalize(self.velocity); 182 | 183 | WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); 184 | WriteByte (MSG_BROADCAST, TE_TAREXPLOSION); 185 | WriteCoord (MSG_BROADCAST, self.origin_x); 186 | WriteCoord (MSG_BROADCAST, self.origin_y); 187 | WriteCoord (MSG_BROADCAST, self.origin_z); 188 | 189 | BecomeExplosion (); 190 | }; 191 | 192 | //============================================================================= 193 | /* 194 | In the United States and elsewhere, the term "tarbaby" has been considered a 195 | racial slur. We've removed it in as many places as possible, but to ensure 196 | backwards compatability with Quake, we had to retain the code and filenames 197 | below. 198 | 199 | For more info: 200 | 201 | https://en.wikipedia.org/wiki/Tar-Baby 202 | http://content.time.com/time/nation/article/0,8599,1221764,00.html 203 | 204 | -- dumptruck_ds 205 | */ 206 | //============================================================================= 207 | 208 | /*QUAKED monster_tarbaby (1 0 0) (-16 -16 -24) (16 16 24) Ambush 209 | */ 210 | void() monster_tarbaby = 211 | { 212 | if (deathmatch) 213 | { 214 | remove(self); 215 | return; 216 | } 217 | precache_model2 ("progs/tarbaby.mdl"); 218 | 219 | precache_sound2 ("blob/death1.wav"); 220 | precache_sound2 ("blob/hit1.wav"); 221 | precache_sound2 ("blob/land1.wav"); 222 | precache_sound2 ("blob/sight1.wav"); 223 | 224 | self.solid = SOLID_SLIDEBOX; 225 | self.movetype = MOVETYPE_STEP; 226 | 227 | setmodel (self, "progs/tarbaby.mdl"); 228 | 229 | setsize (self, '-16 -16 -24', '16 16 40'); 230 | self.health = 80; 231 | 232 | self.th_stand = tbaby_stand1; 233 | self.th_walk = tbaby_walk1; 234 | self.th_run = tbaby_run1; 235 | self.th_missile = tbaby_jump1; 236 | self.th_melee = tbaby_jump1; 237 | self.th_die = tbaby_die1; 238 | 239 | walkmonster_start (); 240 | }; 241 | 242 | /*QUAKED monster_spawn (1 0 0) (-16 -16 -24) (16 16 24) Ambush 243 | */ 244 | void() monster_spawn = // see comment on line 188 above -- dumptruck_ds 245 | { 246 | self.classname = "monster_tarbaby"; 247 | monster_tarbaby(); 248 | }; 249 | -------------------------------------------------------------------------------- /src/sprites.qc: -------------------------------------------------------------------------------- 1 | 2 | // these are the only sprites still in the game... 3 | 4 | $spritename s_explod 5 | $type vp_parallel 6 | $load id1/gfx/sprites/explod03.lbm 7 | $frame 24 24 56 56 8 | $frame 120 24 56 56 9 | $frame 216 24 56 56 10 | $frame 24 88 56 56 11 | $frame 120 88 56 56 12 | $frame 216 88 56 56 13 | 14 | 15 | $spritename s_bubble 16 | $type vp_parallel 17 | $load id1/gfx/sprites/bubble.lbm 18 | $frame 16 16 16 16 19 | $frame 40 16 16 16 20 | 21 | 22 | $spritename s_light 23 | $type vp_parallel 24 | $load id1/gfx/sprites/light.lbm 25 | $frame 104 32 32 32 26 | 27 | -------------------------------------------------------------------------------- /src/subs.qc: -------------------------------------------------------------------------------- 1 | 2 | 3 | void() SUB_Null = {}; 4 | 5 | void() SUB_Remove = {remove(self);}; 6 | 7 | 8 | /* 9 | QuakeEd only writes a single float for angles (bad idea), so up and down are 10 | just constant angles. 11 | */ 12 | void() SetMovedir = 13 | { 14 | if (self.angles == '0 -1 0') 15 | self.movedir = '0 0 1'; 16 | else if (self.angles == '0 -2 0') 17 | self.movedir = '0 0 -1'; 18 | else 19 | { 20 | makevectors (self.angles); 21 | self.movedir = v_forward; 22 | } 23 | 24 | self.angles = '0 0 0'; 25 | }; 26 | 27 | /* 28 | ================ 29 | InitTrigger 30 | ================ 31 | */ 32 | void() InitTrigger = 33 | { 34 | // trigger angles are used for one-way touches. An angle of 0 is assumed 35 | // to mean no restrictions, so use a yaw of 360 instead. 36 | if (self.angles != '0 0 0') 37 | SetMovedir (); 38 | self.solid = SOLID_TRIGGER; 39 | setmodel (self, self.model); // set size and link into world 40 | self.movetype = MOVETYPE_NONE; 41 | self.modelindex = 0; 42 | self.model = ""; 43 | }; 44 | 45 | /* 46 | ============= 47 | SUB_CalcMove 48 | 49 | calculate self.velocity and self.nextthink to reach dest from 50 | self.origin traveling at speed 51 | =============== 52 | */ 53 | void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt = 54 | { 55 | local entity stemp; 56 | stemp = self; 57 | self = ent; 58 | 59 | SUB_CalcMove (tdest, tspeed, func); 60 | self = stemp; 61 | }; 62 | 63 | void(vector tdest, float tspeed, void() func) SUB_CalcMove = 64 | { 65 | local vector vdestdelta; 66 | local float len, traveltime; 67 | 68 | if (!tspeed) 69 | objerror("No speed is defined!"); 70 | 71 | self.think1 = func; 72 | self.finaldest = tdest; 73 | self.think = SUB_CalcMoveDone; 74 | 75 | if (tdest == self.origin) 76 | { 77 | self.velocity = '0 0 0'; 78 | self.nextthink = self.ltime + 0.1; 79 | return; 80 | } 81 | 82 | // set destdelta to the vector needed to move 83 | vdestdelta = tdest - self.origin; 84 | 85 | // calculate length of vector 86 | len = vlen (vdestdelta); 87 | 88 | // divide by speed to get time to reach dest 89 | traveltime = len / tspeed; 90 | 91 | if (traveltime < 0.1) 92 | { 93 | self.velocity = '0 0 0'; 94 | self.nextthink = self.ltime + 0.1; 95 | return; 96 | } 97 | 98 | // set nextthink to trigger a think when dest is reached 99 | self.nextthink = self.ltime + traveltime; 100 | 101 | // scale the destdelta vector by the time spent traveling to get velocity 102 | self.velocity = vdestdelta * (1/traveltime); // qcc won't take vec/float 103 | }; 104 | 105 | /* 106 | ============ 107 | After moving, set origin to exact final destination 108 | ============ 109 | */ 110 | void() SUB_CalcMoveDone = 111 | { 112 | setorigin(self, self.finaldest); 113 | self.velocity = '0 0 0'; 114 | self.nextthink = -1; 115 | if (self.think1) 116 | self.think1(); 117 | }; 118 | 119 | 120 | /* 121 | ============= 122 | SUB_CalcAngleMove 123 | 124 | calculate self.avelocity and self.nextthink to reach destangle from 125 | self.angles rotating 126 | 127 | The calling function should make sure self.think is valid 128 | =============== 129 | */ 130 | void(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt = 131 | { 132 | local entity stemp; 133 | stemp = self; 134 | self = ent; 135 | SUB_CalcAngleMove (destangle, tspeed, func); 136 | self = stemp; 137 | }; 138 | 139 | void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove = 140 | { 141 | local vector destdelta; 142 | local float len, traveltime; 143 | 144 | if (!tspeed) 145 | objerror("No speed is defined!"); 146 | 147 | // set destdelta to the vector needed to move 148 | destdelta = destangle - self.angles; 149 | 150 | // calculate length of vector 151 | len = vlen (destdelta); 152 | 153 | // divide by speed to get time to reach dest 154 | traveltime = len / tspeed; 155 | 156 | // set nextthink to trigger a think when dest is reached 157 | self.nextthink = self.ltime + traveltime; 158 | 159 | // scale the destdelta vector by the time spent traveling to get velocity 160 | self.avelocity = destdelta * (1 / traveltime); 161 | 162 | self.think1 = func; 163 | self.finalangle = destangle; 164 | self.think = SUB_CalcAngleMoveDone; 165 | }; 166 | 167 | /* 168 | ============ 169 | After rotating, set angle to exact final angle 170 | ============ 171 | */ 172 | void() SUB_CalcAngleMoveDone = 173 | { 174 | self.angles = self.finalangle; 175 | self.avelocity = '0 0 0'; 176 | self.nextthink = -1; 177 | if (self.think1) 178 | self.think1(); 179 | }; 180 | 181 | 182 | //============================================================================= 183 | 184 | void() DelayThink = 185 | { 186 | activator = self.enemy; 187 | SUB_UseTargets (); 188 | remove(self); 189 | }; 190 | 191 | /* From Custents and modified for smp (added targets2-4, killtarget2) 192 | ============================== 193 | SUB_UseTargets 194 | 195 | the global "activator" should be set to the entity that initiated the firing. 196 | 197 | If self.delay is set, a DelayedUse entity will be created that will actually 198 | do the SUB_UseTargets after that many seconds have passed. 199 | 200 | Centerprints any self.message to the activator. 201 | 202 | Removes all entities with a targetname that match self.killtarget, 203 | or killtarget2, so some events can remove other triggers. 204 | 205 | Search for (string)targetname in all entities that match (string)self.target, self.target2, self.target3, 206 | or self.target4 and use their .use function. 207 | ============================== 208 | */ 209 | void(string matchstring, .string matchfield) SUB_UseSpecificTarget = 210 | { 211 | local entity t, stemp, otemp, act; 212 | 213 | act = activator; 214 | t = find (world, matchfield, matchstring); 215 | while ( t != world ) 216 | { 217 | stemp = self; 218 | otemp = other; 219 | self = t; 220 | other = stemp; 221 | if (self.use != SUB_Null) 222 | { 223 | if (self.use) 224 | { 225 | lastnameused = matchstring; 226 | self.use (); 227 | } 228 | } 229 | self = stemp; 230 | other = otemp; 231 | activator = act; 232 | t = find (t, matchfield, matchstring); 233 | } 234 | }; 235 | 236 | void() SUB_UseTargets = 237 | { 238 | // local entity t, stemp, otemp, act; 239 | local entity t; 240 | 241 | // 242 | // check for a delay 243 | // 244 | if (self.delay) 245 | { 246 | // create a temp object to fire at a later time 247 | t = spawn(); 248 | t.classname = "DelayedUse"; 249 | t.nextthink = time + self.delay; 250 | t.think = DelayThink; 251 | t.enemy = activator; 252 | t.message = self.message; 253 | t.killtarget = self.killtarget; 254 | t.killtarget2 = self.killtarget2; 255 | t.target = self.target; 256 | t.target2 = self.target2; 257 | t.target3 = self.target3; 258 | t.target4 = self.target4; 259 | return; 260 | } 261 | 262 | 263 | // 264 | // print the message 265 | // 266 | if (activator.classname == "player" && self.message != "") 267 | { 268 | centerprint (activator, self.message); 269 | if (!self.noise) 270 | sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); 271 | } 272 | 273 | // 274 | // kill the killtarget entities 275 | // 276 | if (self.killtarget != "") 277 | { 278 | t = find(world, targetname, self.killtarget); 279 | while(t != world) 280 | { 281 | remove(t); 282 | t = find(t, targetname, self.killtarget); 283 | } 284 | } 285 | 286 | // 287 | // kill the killtaget2 entities 288 | // 289 | if (self.killtarget2 != "") 290 | { 291 | t = find(world, targetname, self.killtarget2); 292 | while(t != world) 293 | { 294 | remove(t); 295 | t = find(t, targetname, self.killtarget2); 296 | } 297 | } 298 | 299 | // 300 | // fire targets 301 | // 302 | 303 | // target 1 304 | if (self.target != "") 305 | { 306 | SUB_UseSpecificTarget(self.target, targetname); 307 | } 308 | 309 | // target 2 310 | if (self.target2 != "") 311 | { 312 | SUB_UseSpecificTarget(self.target2, targetname); 313 | } 314 | 315 | // target 3 316 | if (self.target3 != "") 317 | { 318 | SUB_UseSpecificTarget(self.target3, targetname); 319 | } 320 | 321 | // target 4 322 | if (self.target4 != "") 323 | { 324 | SUB_UseSpecificTarget(self.target4, targetname); 325 | } 326 | }; 327 | 328 | void(string matchstring) SUB_UseName = 329 | { 330 | SUB_UseSpecificTarget(matchstring, targetname); 331 | }; 332 | // ### end of Custents triggering code 333 | 334 | /* 335 | The code below was created by iw for progs_dump and has been modified for smp 336 | ================ 337 | SUB_UseAndForgetTargets 338 | 339 | This calls SUB_UseTargets, then clears all of self's fields that cause 340 | SUB_UseTargets to do things. The intention is that if SUB_UseTargets 341 | gets called again in the future, it won't do anything. Call this 342 | function if you want an entity to fire its targets just this once, and 343 | never again. 344 | 345 | Note that this function relies on the fact that SUB_UseTargets has 346 | already been modified to work around the engine bug involving tests of 347 | the form 'if (string)', i.e. that the tests in SUB_UseTargets are now of 348 | the form 'if (string != "")'. -- iw 349 | ================ 350 | */ 351 | void() SUB_UseAndForgetTargets = 352 | { 353 | SUB_UseTargets (); 354 | 355 | self.delay = 0; 356 | self.killtarget = ""; 357 | // self.killtarget2 = ""; 358 | self.message = ""; 359 | self.target = ""; 360 | // self.target2 = ""; 361 | // self.target3 = ""; 362 | // self.target4 = ""; 363 | }; 364 | 365 | /* 366 | ================ 367 | SUB_FieldIsTargeted 368 | 369 | Return TRUE if the "fld" field of this entity is non-empty and matches 370 | the target (or target2/3/4 or pain_target) field of any other entity, 371 | otherwise return FALSE. -- iw 372 | ================ 373 | */ 374 | float(.string fld) SUB_FieldIsTargeted = 375 | { 376 | if (self.fld == "") 377 | return FALSE; 378 | 379 | // the following function calls are staggered to avoid the silly 380 | // "return value conflict" problem with traditional compilers -- iw 381 | 382 | if (find (world, target, self.fld) != world) 383 | return TRUE; 384 | 385 | if (find (world, target2, self.fld) != world) 386 | return TRUE; 387 | 388 | if (find (world, target3, self.fld) != world) 389 | return TRUE; 390 | 391 | if (find (world, target4, self.fld) != world) 392 | return TRUE; 393 | 394 | return FALSE; 395 | }; 396 | 397 | /* 398 | ================ 399 | SUB_IsTargeted 400 | 401 | Return TRUE if the targetname (or targetname2/3/4) field of this entity 402 | is non-empty and matches the target (or target2/3/4 or pain_target) 403 | field of any other entity, otherwise return FALSE. -- iw 404 | ================ 405 | */ 406 | float() SUB_IsTargeted = 407 | { 408 | // the following function calls are staggered to avoid the silly 409 | // "return value conflict" problem with traditional compilers -- iw 410 | 411 | if (SUB_FieldIsTargeted (targetname)) 412 | return TRUE; 413 | 414 | return FALSE; 415 | }; 416 | 417 | /* 418 | 419 | in nightmare mode, all attack_finished times become 0 420 | some monsters refire twice automatically 421 | 422 | */ 423 | 424 | void(float normal) SUB_AttackFinished = 425 | { 426 | self.cnt = 0; // refire count for nightmare 427 | if (skill != 3) 428 | self.attack_finished = time + normal; 429 | }; 430 | 431 | float (entity targ) visible; 432 | 433 | void (void() thinkst) SUB_CheckRefire = 434 | { 435 | if (skill != 3) 436 | return; 437 | if (self.cnt == 1) 438 | return; 439 | if (!visible (self.enemy)) 440 | return; 441 | self.cnt = 1; 442 | self.think = thinkst; 443 | }; 444 | -------------------------------------------------------------------------------- /src/wizard.qc: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | WIZARD 5 | 6 | ============================================================================== 7 | */ 8 | 9 | $cd id1/models/a_wizard 10 | $origin 0 0 24 11 | $base wizbase 12 | $skin wizbase 13 | 14 | $frame hover1 hover2 hover3 hover4 hover5 hover6 hover7 hover8 15 | $frame hover9 hover10 hover11 hover12 hover13 hover14 hover15 16 | 17 | $frame fly1 fly2 fly3 fly4 fly5 fly6 fly7 fly8 fly9 fly10 18 | $frame fly11 fly12 fly13 fly14 19 | 20 | $frame magatt1 magatt2 magatt3 magatt4 magatt5 magatt6 magatt7 21 | $frame magatt8 magatt9 magatt10 magatt11 magatt12 magatt13 22 | 23 | $frame pain1 pain2 pain3 pain4 24 | 25 | $frame death1 death2 death3 death4 death5 death6 death7 death8 26 | 27 | /* 28 | ============================================================================== 29 | 30 | WIZARD 31 | 32 | If the player moves behind cover before the missile is launched, launch it 33 | at the last visible spot with no velocity leading, in hopes that the player 34 | will duck back out and catch it. 35 | ============================================================================== 36 | */ 37 | 38 | /* 39 | ============= 40 | LaunchMissile 41 | 42 | Sets the given entities velocity and angles so that it will hit self.enemy 43 | if self.enemy maintains it's current velocity 44 | 0.1 is moderately accurate, 0.0 is totally accurate 45 | ============= 46 | */ 47 | void(entity missile, float mspeed, float accuracy) LaunchMissile = 48 | { 49 | local vector vec, move; 50 | local float fly; 51 | 52 | makevectors (self.angles); 53 | 54 | // set missile speed 55 | vec = self.enemy.origin + self.enemy.mins + self.enemy.size * 0.7 - missile.origin; 56 | 57 | // calc aproximate time for missile to reach vec 58 | fly = vlen (vec) / mspeed; 59 | 60 | // get the entities xy velocity 61 | move = self.enemy.velocity; 62 | move_z = 0; 63 | 64 | // project the target forward in time 65 | vec = vec + move * fly; 66 | 67 | vec = normalize(vec); 68 | vec = vec + accuracy*v_up*(random()- 0.5) + accuracy*v_right*(random()- 0.5); 69 | 70 | missile.velocity = vec * mspeed; 71 | 72 | missile.angles = '0 0 0'; 73 | missile.angles_y = vectoyaw(missile.velocity); 74 | 75 | // set missile duration 76 | missile.nextthink = time + 5; 77 | missile.think = SUB_Remove; 78 | }; 79 | 80 | 81 | void() wiz_run1; 82 | void() wiz_side1; 83 | 84 | /* 85 | ================= 86 | WizardCheckAttack 87 | ================= 88 | */ 89 | float() WizardCheckAttack = 90 | { 91 | local vector spot1, spot2; 92 | local entity targ; 93 | local float chance; 94 | 95 | if (time < self.attack_finished) 96 | return FALSE; 97 | if (!enemy_vis) 98 | return FALSE; 99 | 100 | if (enemy_range == RANGE_FAR) 101 | { 102 | if (self.attack_state != AS_STRAIGHT) 103 | { 104 | self.attack_state = AS_STRAIGHT; 105 | wiz_run1 (); 106 | } 107 | return FALSE; 108 | } 109 | 110 | targ = self.enemy; 111 | 112 | // see if any entities are in the way of the shot 113 | spot1 = self.origin + self.view_ofs; 114 | spot2 = targ.origin + targ.view_ofs; 115 | 116 | traceline (spot1, spot2, FALSE, self); 117 | 118 | if (trace_ent != targ) 119 | { // don't have a clear shot, so move to a side 120 | if (self.attack_state != AS_STRAIGHT) 121 | { 122 | self.attack_state = AS_STRAIGHT; 123 | wiz_run1 (); 124 | } 125 | return FALSE; 126 | } 127 | 128 | if (enemy_range == RANGE_MELEE) 129 | chance = 0.9; 130 | else if (enemy_range == RANGE_NEAR) 131 | chance = 0.6; 132 | else if (enemy_range == RANGE_MID) 133 | chance = 0.2; 134 | else 135 | chance = 0; 136 | 137 | if (random () < chance) 138 | { 139 | self.attack_state = AS_MISSILE; 140 | return TRUE; 141 | } 142 | 143 | if (enemy_range == RANGE_MID) 144 | { 145 | if (self.attack_state != AS_STRAIGHT) 146 | { 147 | self.attack_state = AS_STRAIGHT; 148 | wiz_run1 (); 149 | } 150 | } 151 | else 152 | { 153 | if (self.attack_state != AS_SLIDING) 154 | { 155 | self.attack_state = AS_SLIDING; 156 | wiz_side1 (); 157 | } 158 | } 159 | 160 | return FALSE; 161 | }; 162 | 163 | /* 164 | ================= 165 | WizardAttackFinished 166 | ================= 167 | */ 168 | void() WizardAttackFinished = 169 | { 170 | if (enemy_range >= RANGE_MID || !enemy_vis) 171 | { 172 | self.attack_state = AS_STRAIGHT; 173 | self.think = wiz_run1; 174 | } 175 | else 176 | { 177 | self.attack_state = AS_SLIDING; 178 | self.think = wiz_side1; 179 | } 180 | }; 181 | 182 | /* 183 | ============================================================================== 184 | 185 | FAST ATTACKS 186 | 187 | ============================================================================== 188 | */ 189 | 190 | void() Wiz_FastFire = 191 | { 192 | local vector vec; 193 | local vector dst; 194 | 195 | if (self.owner.health > 0) 196 | { 197 | self.owner.effects = self.owner.effects | EF_MUZZLEFLASH; 198 | 199 | makevectors (self.enemy.angles); 200 | dst = self.enemy.origin - 13*self.movedir; 201 | 202 | vec = normalize(dst - self.origin); 203 | sound (self, CHAN_WEAPON, "wizard/wattack.wav", 1, ATTN_NORM); 204 | launch_spike (self.origin, vec); 205 | newmis.velocity = vec*600; 206 | newmis.owner = self.owner; 207 | newmis.classname = "wizspike"; 208 | setmodel (newmis, "progs/w_spike.mdl"); 209 | setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); 210 | } 211 | 212 | remove (self); 213 | }; 214 | 215 | 216 | void() Wiz_StartFast = 217 | { 218 | local entity missile; 219 | 220 | sound (self, CHAN_WEAPON, "wizard/wattack.wav", 1, ATTN_NORM); 221 | self.v_angle = self.angles; 222 | makevectors (self.angles); 223 | 224 | missile = spawn (); 225 | missile.owner = self; 226 | missile.nextthink = time + 0.6; 227 | setsize (missile, '0 0 0', '0 0 0'); 228 | setorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right*14); 229 | missile.enemy = self.enemy; 230 | missile.nextthink = time + 0.8; 231 | missile.think = Wiz_FastFire; 232 | missile.movedir = v_right; 233 | 234 | missile = spawn (); 235 | missile.owner = self; 236 | missile.nextthink = time + 1; 237 | setsize (missile, '0 0 0', '0 0 0'); 238 | setorigin (missile, self.origin + '0 0 30' + v_forward*14 + v_right* -14); 239 | missile.enemy = self.enemy; 240 | missile.nextthink = time + 0.3; 241 | missile.think = Wiz_FastFire; 242 | missile.movedir = VEC_ORIGIN - v_right; 243 | }; 244 | 245 | 246 | 247 | void() Wiz_idlesound = 248 | { 249 | local float wr; 250 | wr = random() * 5; 251 | 252 | if (self.waitmin < time) 253 | { 254 | self.waitmin = time + 2; 255 | if (wr > 4.5) 256 | sound (self, CHAN_VOICE, "wizard/widle1.wav", 1, ATTN_IDLE); 257 | if (wr < 1.5) 258 | sound (self, CHAN_VOICE, "wizard/widle2.wav", 1, ATTN_IDLE); 259 | } 260 | return; 261 | }; 262 | 263 | void() wiz_stand1 =[ $hover1, wiz_stand2 ] {ai_stand();}; 264 | void() wiz_stand2 =[ $hover2, wiz_stand3 ] {ai_stand();}; 265 | void() wiz_stand3 =[ $hover3, wiz_stand4 ] {ai_stand();}; 266 | void() wiz_stand4 =[ $hover4, wiz_stand5 ] {ai_stand();}; 267 | void() wiz_stand5 =[ $hover5, wiz_stand6 ] {ai_stand();}; 268 | void() wiz_stand6 =[ $hover6, wiz_stand7 ] {ai_stand();}; 269 | void() wiz_stand7 =[ $hover7, wiz_stand8 ] {ai_stand();}; 270 | void() wiz_stand8 =[ $hover8, wiz_stand1 ] {ai_stand();}; 271 | 272 | void() wiz_walk1 =[ $hover1, wiz_walk2 ] {ai_walk(8); 273 | Wiz_idlesound();}; 274 | void() wiz_walk2 =[ $hover2, wiz_walk3 ] {ai_walk(8);}; 275 | void() wiz_walk3 =[ $hover3, wiz_walk4 ] {ai_walk(8);}; 276 | void() wiz_walk4 =[ $hover4, wiz_walk5 ] {ai_walk(8);}; 277 | void() wiz_walk5 =[ $hover5, wiz_walk6 ] {ai_walk(8);}; 278 | void() wiz_walk6 =[ $hover6, wiz_walk7 ] {ai_walk(8);}; 279 | void() wiz_walk7 =[ $hover7, wiz_walk8 ] {ai_walk(8);}; 280 | void() wiz_walk8 =[ $hover8, wiz_walk1 ] {ai_walk(8);}; 281 | 282 | void() wiz_side1 =[ $hover1, wiz_side2 ] {ai_run(8); 283 | Wiz_idlesound();}; 284 | void() wiz_side2 =[ $hover2, wiz_side3 ] {ai_run(8);}; 285 | void() wiz_side3 =[ $hover3, wiz_side4 ] {ai_run(8);}; 286 | void() wiz_side4 =[ $hover4, wiz_side5 ] {ai_run(8);}; 287 | void() wiz_side5 =[ $hover5, wiz_side6 ] {ai_run(8);}; 288 | void() wiz_side6 =[ $hover6, wiz_side7 ] {ai_run(8);}; 289 | void() wiz_side7 =[ $hover7, wiz_side8 ] {ai_run(8);}; 290 | void() wiz_side8 =[ $hover8, wiz_side1 ] {ai_run(8);}; 291 | 292 | void() wiz_run1 =[ $fly1, wiz_run2 ] {ai_run(16); 293 | Wiz_idlesound(); 294 | }; 295 | void() wiz_run2 =[ $fly2, wiz_run3 ] {ai_run(16);}; 296 | void() wiz_run3 =[ $fly3, wiz_run4 ] {ai_run(16);}; 297 | void() wiz_run4 =[ $fly4, wiz_run5 ] {ai_run(16);}; 298 | void() wiz_run5 =[ $fly5, wiz_run6 ] {ai_run(16);}; 299 | void() wiz_run6 =[ $fly6, wiz_run7 ] {ai_run(16);}; 300 | void() wiz_run7 =[ $fly7, wiz_run8 ] {ai_run(16);}; 301 | void() wiz_run8 =[ $fly8, wiz_run9 ] {ai_run(16);}; 302 | void() wiz_run9 =[ $fly9, wiz_run10 ] {ai_run(16);}; 303 | void() wiz_run10 =[ $fly10, wiz_run11 ] {ai_run(16);}; 304 | void() wiz_run11 =[ $fly11, wiz_run12 ] {ai_run(16);}; 305 | void() wiz_run12 =[ $fly12, wiz_run13 ] {ai_run(16);}; 306 | void() wiz_run13 =[ $fly13, wiz_run14 ] {ai_run(16);}; 307 | void() wiz_run14 =[ $fly14, wiz_run1 ] {ai_run(16);}; 308 | 309 | void() wiz_fast1 =[ $magatt1, wiz_fast2 ] {ai_face();Wiz_StartFast();}; 310 | void() wiz_fast2 =[ $magatt2, wiz_fast3 ] {ai_face();}; 311 | void() wiz_fast3 =[ $magatt3, wiz_fast4 ] {ai_face();}; 312 | void() wiz_fast4 =[ $magatt4, wiz_fast5 ] {ai_face();}; 313 | void() wiz_fast5 =[ $magatt5, wiz_fast6 ] {ai_face();}; 314 | void() wiz_fast6 =[ $magatt6, wiz_fast7 ] {ai_face();}; 315 | void() wiz_fast7 =[ $magatt5, wiz_fast8 ] {ai_face();}; 316 | void() wiz_fast8 =[ $magatt4, wiz_fast9 ] {ai_face();}; 317 | void() wiz_fast9 =[ $magatt3, wiz_fast10 ] {ai_face();}; 318 | void() wiz_fast10 =[ $magatt2, wiz_run1 ] {ai_face();SUB_AttackFinished(2);WizardAttackFinished ();}; 319 | 320 | void() wiz_pain1 =[ $pain1, wiz_pain2 ] {}; 321 | void() wiz_pain2 =[ $pain2, wiz_pain3 ] {}; 322 | void() wiz_pain3 =[ $pain3, wiz_pain4 ] {}; 323 | void() wiz_pain4 =[ $pain4, wiz_run1 ] {}; 324 | 325 | void() wiz_death1 =[ $death1, wiz_death2 ] { 326 | 327 | self.velocity_x = -200 + 400*random(); 328 | self.velocity_y = -200 + 400*random(); 329 | self.velocity_z = 100 + 100*random(); 330 | self.flags = self.flags - (self.flags & FL_ONGROUND); 331 | sound (self, CHAN_VOICE, "wizard/wdeath.wav", 1, ATTN_NORM); 332 | }; 333 | void() wiz_death2 =[ $death2, wiz_death3 ] {}; 334 | void() wiz_death3 =[ $death3, wiz_death4 ]{self.solid = SOLID_NOT;}; 335 | void() wiz_death4 =[ $death4, wiz_death5 ] {}; 336 | void() wiz_death5 =[ $death5, wiz_death6 ] {}; 337 | void() wiz_death6 =[ $death6, wiz_death7 ] {}; 338 | void() wiz_death7 =[ $death7, wiz_death8 ] {}; 339 | void() wiz_death8 =[ $death8, wiz_death8 ] {}; 340 | 341 | void() wiz_die = 342 | { 343 | // check for gib 344 | if (self.health < -40) 345 | { 346 | ThrowHead ("progs/h_wizard.mdl", self.health); 347 | ThrowGib ("progs/gib2.mdl", self.health); 348 | ThrowGib ("progs/gib2.mdl", self.health); 349 | ThrowGib ("progs/gib2.mdl", self.health); 350 | // spinstagib sounds 351 | if ((cvar("fraglimit") == 666) || (cvar("fraglimit") == 333)) 352 | { 353 | GibSoundsRandom(); 354 | return; 355 | } 356 | sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM); 357 | return; 358 | } 359 | 360 | wiz_death1 (); 361 | }; 362 | 363 | 364 | void(entity attacker, float damage) Wiz_Pain = 365 | { 366 | sound (self, CHAN_VOICE, "wizard/wpain.wav", 1, ATTN_NORM); 367 | if (random()*70 > damage) 368 | return; // didn't flinch 369 | 370 | wiz_pain1 (); 371 | }; 372 | 373 | 374 | void() Wiz_Missile = 375 | { 376 | wiz_fast1(); 377 | }; 378 | 379 | /*QUAKED monster_wizard (1 0 0) (-16 -16 -24) (16 16 40) Ambush 380 | */ 381 | void() monster_wizard = 382 | { 383 | if (deathmatch) 384 | { 385 | remove(self); 386 | return; 387 | } 388 | precache_model ("progs/wizard.mdl"); 389 | precache_model ("progs/h_wizard.mdl"); 390 | precache_model ("progs/w_spike.mdl"); 391 | 392 | precache_sound ("wizard/hit.wav"); // used by c code 393 | precache_sound ("wizard/wattack.wav"); 394 | precache_sound ("wizard/wdeath.wav"); 395 | precache_sound ("wizard/widle1.wav"); 396 | precache_sound ("wizard/widle2.wav"); 397 | precache_sound ("wizard/wpain.wav"); 398 | precache_sound ("wizard/wsight.wav"); 399 | 400 | self.solid = SOLID_SLIDEBOX; 401 | self.movetype = MOVETYPE_STEP; 402 | 403 | setmodel (self, "progs/wizard.mdl"); 404 | 405 | setsize (self, '-16 -16 -24', '16 16 40'); 406 | self.health = 80; 407 | 408 | self.th_stand = wiz_stand1; 409 | self.th_walk = wiz_walk1; 410 | self.th_run = wiz_run1; 411 | self.th_missile = Wiz_Missile; 412 | self.th_pain = Wiz_Pain; 413 | self.th_die = wiz_die; 414 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten start 415 | self.touch = monster_touch; 416 | // 1998-09-16 Sliding/not-jumping on monsters/boxes/players fix by Maddes/Kryten end 417 | 418 | flymonster_start (); 419 | }; 420 | -------------------------------------------------------------------------------- /src/world.qc: -------------------------------------------------------------------------------- 1 | 2 | void() InitBodyQue; 3 | 4 | 5 | void() main = 6 | { 7 | dprint ("main function\n"); 8 | 9 | // these are just commands the the prog compiler to copy these files 10 | 11 | precache_file ("progs.dat"); 12 | precache_file ("gfx.wad"); 13 | precache_file ("quake.rc"); 14 | precache_file ("default.cfg"); 15 | 16 | precache_file ("end1.bin"); 17 | precache_file2 ("end2.bin"); 18 | 19 | precache_file ("demo1.dem"); 20 | precache_file ("demo2.dem"); 21 | precache_file ("demo3.dem"); 22 | 23 | // 24 | // these are all of the lumps from the cached.ls files 25 | // 26 | precache_file ("gfx/palette.lmp"); 27 | precache_file ("gfx/colormap.lmp"); 28 | 29 | precache_file2 ("gfx/pop.lmp"); 30 | 31 | precache_file ("gfx/complete.lmp"); 32 | precache_file ("gfx/inter.lmp"); 33 | 34 | precache_file ("gfx/ranking.lmp"); 35 | precache_file ("gfx/vidmodes.lmp"); 36 | precache_file ("gfx/finale.lmp"); 37 | precache_file ("gfx/conback.lmp"); 38 | precache_file ("gfx/qplaque.lmp"); 39 | 40 | precache_file ("gfx/menudot1.lmp"); 41 | precache_file ("gfx/menudot2.lmp"); 42 | precache_file ("gfx/menudot3.lmp"); 43 | precache_file ("gfx/menudot4.lmp"); 44 | precache_file ("gfx/menudot5.lmp"); 45 | precache_file ("gfx/menudot6.lmp"); 46 | 47 | precache_file ("gfx/menuplyr.lmp"); 48 | precache_file ("gfx/bigbox.lmp"); 49 | precache_file ("gfx/dim_modm.lmp"); 50 | precache_file ("gfx/dim_drct.lmp"); 51 | precache_file ("gfx/dim_ipx.lmp"); 52 | precache_file ("gfx/dim_tcp.lmp"); 53 | precache_file ("gfx/dim_mult.lmp"); 54 | precache_file ("gfx/mainmenu.lmp"); 55 | 56 | precache_file ("gfx/box_tl.lmp"); 57 | precache_file ("gfx/box_tm.lmp"); 58 | precache_file ("gfx/box_tr.lmp"); 59 | 60 | precache_file ("gfx/box_ml.lmp"); 61 | precache_file ("gfx/box_mm.lmp"); 62 | precache_file ("gfx/box_mm2.lmp"); 63 | precache_file ("gfx/box_mr.lmp"); 64 | 65 | precache_file ("gfx/box_bl.lmp"); 66 | precache_file ("gfx/box_bm.lmp"); 67 | precache_file ("gfx/box_br.lmp"); 68 | 69 | precache_file ("gfx/sp_menu.lmp"); 70 | precache_file ("gfx/ttl_sgl.lmp"); 71 | precache_file ("gfx/ttl_main.lmp"); 72 | precache_file ("gfx/ttl_cstm.lmp"); 73 | 74 | precache_file ("gfx/mp_menu.lmp"); 75 | 76 | precache_file ("gfx/netmen1.lmp"); 77 | precache_file ("gfx/netmen2.lmp"); 78 | precache_file ("gfx/netmen3.lmp"); 79 | precache_file ("gfx/netmen4.lmp"); 80 | precache_file ("gfx/netmen5.lmp"); 81 | 82 | precache_file ("gfx/sell.lmp"); 83 | 84 | precache_file ("gfx/help0.lmp"); 85 | precache_file ("gfx/help1.lmp"); 86 | precache_file ("gfx/help2.lmp"); 87 | precache_file ("gfx/help3.lmp"); 88 | precache_file ("gfx/help4.lmp"); 89 | precache_file ("gfx/help5.lmp"); 90 | 91 | precache_file ("gfx/pause.lmp"); 92 | precache_file ("gfx/loading.lmp"); 93 | 94 | precache_file ("gfx/p_option.lmp"); 95 | precache_file ("gfx/p_load.lmp"); 96 | precache_file ("gfx/p_save.lmp"); 97 | precache_file ("gfx/p_multi.lmp"); 98 | 99 | // sounds loaded by C code 100 | precache_sound ("misc/menu1.wav"); 101 | precache_sound ("misc/menu2.wav"); 102 | precache_sound ("misc/menu3.wav"); 103 | 104 | precache_sound ("ambience/water1.wav"); 105 | precache_sound ("ambience/wind2.wav"); 106 | 107 | // shareware 108 | precache_file ("maps/start.bsp"); 109 | 110 | precache_file ("maps/e1m1.bsp"); 111 | precache_file ("maps/e1m2.bsp"); 112 | precache_file ("maps/e1m3.bsp"); 113 | precache_file ("maps/e1m4.bsp"); 114 | precache_file ("maps/e1m5.bsp"); 115 | precache_file ("maps/e1m6.bsp"); 116 | precache_file ("maps/e1m7.bsp"); 117 | precache_file ("maps/e1m8.bsp"); 118 | 119 | // registered 120 | precache_file2 ("gfx/pop.lmp"); 121 | 122 | precache_file2 ("maps/e2m1.bsp"); 123 | precache_file2 ("maps/e2m2.bsp"); 124 | precache_file2 ("maps/e2m3.bsp"); 125 | precache_file2 ("maps/e2m4.bsp"); 126 | precache_file2 ("maps/e2m5.bsp"); 127 | precache_file2 ("maps/e2m6.bsp"); 128 | precache_file2 ("maps/e2m7.bsp"); 129 | 130 | precache_file2 ("maps/e3m1.bsp"); 131 | precache_file2 ("maps/e3m2.bsp"); 132 | precache_file2 ("maps/e3m3.bsp"); 133 | precache_file2 ("maps/e3m4.bsp"); 134 | precache_file2 ("maps/e3m5.bsp"); 135 | precache_file2 ("maps/e3m6.bsp"); 136 | precache_file2 ("maps/e3m7.bsp"); 137 | 138 | precache_file2 ("maps/e4m1.bsp"); 139 | precache_file2 ("maps/e4m2.bsp"); 140 | precache_file2 ("maps/e4m3.bsp"); 141 | precache_file2 ("maps/e4m4.bsp"); 142 | precache_file2 ("maps/e4m5.bsp"); 143 | precache_file2 ("maps/e4m6.bsp"); 144 | precache_file2 ("maps/e4m7.bsp"); 145 | precache_file2 ("maps/e4m8.bsp"); 146 | 147 | precache_file2 ("maps/end.bsp"); 148 | 149 | precache_file2 ("maps/dm1.bsp"); 150 | precache_file2 ("maps/dm2.bsp"); 151 | precache_file2 ("maps/dm3.bsp"); 152 | precache_file2 ("maps/dm4.bsp"); 153 | precache_file2 ("maps/dm5.bsp"); 154 | precache_file2 ("maps/dm6.bsp"); 155 | }; 156 | 157 | 158 | entity lastspawn; 159 | 160 | //======================= 161 | /*QUAKED worldspawn (0 0 0) ? 162 | Only used for the world entity. 163 | Set message to the level name. 164 | Set sounds to the cd track to play. 165 | 166 | World Types: 167 | 0: medieval 168 | 1: metal 169 | 2: base 170 | */ 171 | //======================= 172 | float startingserverflags; // put serverflags back to map's initial setting - Preach -- dumptruck_ds 173 | 174 | void() worldspawn = 175 | { 176 | // put serverflags back to map's initial setting - Preach 177 | startingserverflags = serverflags; 178 | lastspawn = world; 179 | InitBodyQue (); 180 | 181 | local float gravity_amt; // start gravity setting in worldspawn - dumptruck_ds 182 | local string g; 183 | gravity_amt = self.gravity; 184 | g = ftos(gravity_amt); 185 | 186 | // custom map attributes 187 | if (self.model == "maps/e1m8.bsp") 188 | cvar_set ("sv_gravity", "100"); 189 | else if (self.gravity) 190 | { 191 | cvar_set ("sv_gravity", g); // end gravity setting in worldspawn - dumptruck_ds 192 | } 193 | else 194 | cvar_set ("sv_gravity", "800"); 195 | 196 | // the area based ambient sounds MUST be the first precache_sounds 197 | 198 | // player precaches 199 | W_Precache (); // get weapon precaches 200 | 201 | // sounds used from C physics code 202 | precache_sound ("demon/dland2.wav"); // landing thud 203 | precache_sound ("misc/h2ohit1.wav"); // landing splash 204 | 205 | // setup precaches allways needed 206 | precache_sound ("items/itembk2.wav"); // item respawn sound 207 | precache_sound ("player/plyrjmp8.wav"); // player jump 208 | precache_sound ("player/land.wav"); // player landing 209 | precache_sound ("player/land2.wav"); // player hurt landing 210 | precache_sound ("player/drown1.wav"); // drowning pain 211 | precache_sound ("player/drown2.wav"); // drowning pain 212 | precache_sound ("player/gasp1.wav"); // gasping for air 213 | precache_sound ("player/gasp2.wav"); // taking breath 214 | precache_sound ("player/h2odeath.wav"); // drowning death 215 | 216 | precache_sound ("misc/talk.wav"); // talk 217 | precache_sound ("player/teledth1.wav"); // telefrag 218 | precache_sound ("misc/r_tele1.wav"); // teleport sounds 219 | precache_sound ("misc/r_tele2.wav"); 220 | precache_sound ("misc/r_tele3.wav"); 221 | precache_sound ("misc/r_tele4.wav"); 222 | precache_sound ("misc/r_tele5.wav"); 223 | precache_sound ("weapons/lock4.wav"); // ammo pick up 224 | precache_sound ("weapons/pkup.wav"); // weapon up 225 | precache_sound ("items/armor1.wav"); // armor up 226 | precache_sound ("weapons/lhit.wav"); //lightning 227 | precache_sound ("weapons/lstart.wav"); //lightning start 228 | precache_sound ("items/damage3.wav"); 229 | 230 | precache_sound ("misc/power.wav"); //lightning for boss 231 | 232 | // player gib sounds 233 | precache_sound ("player/gib.wav"); // player gib sound 234 | precache_sound ("player/udeath.wav"); // player gib sound 235 | precache_sound ("player/tornoff2.wav"); // gib sound 236 | 237 | // player pain sounds 238 | 239 | precache_sound ("player/pain1.wav"); 240 | precache_sound ("player/pain2.wav"); 241 | precache_sound ("player/pain3.wav"); 242 | precache_sound ("player/pain4.wav"); 243 | precache_sound ("player/pain5.wav"); 244 | precache_sound ("player/pain6.wav"); 245 | 246 | // player death sounds 247 | precache_sound ("player/death1.wav"); 248 | precache_sound ("player/death2.wav"); 249 | precache_sound ("player/death3.wav"); 250 | precache_sound ("player/death4.wav"); 251 | precache_sound ("player/death5.wav"); 252 | 253 | // ax sounds 254 | precache_sound ("weapons/ax1.wav"); // ax swoosh 255 | precache_sound ("player/axhit1.wav"); // ax hit meat 256 | precache_sound ("player/axhit2.wav"); // ax hit world 257 | 258 | precache_sound ("player/h2ojump.wav"); // player jumping into water 259 | precache_sound ("player/slimbrn2.wav"); // player enter slime 260 | precache_sound ("player/inh2o.wav"); // player enter water 261 | precache_sound ("player/inlava.wav"); // player enter lava 262 | precache_sound ("misc/outwater.wav"); // leaving water sound 263 | 264 | precache_sound ("player/lburn1.wav"); // lava burn 265 | precache_sound ("player/lburn2.wav"); // lava burn 266 | 267 | precache_sound ("misc/water1.wav"); // swimming 268 | precache_sound ("misc/water2.wav"); // swimming 269 | 270 | //Instagib 271 | precache_sound ("blob/death1.wav"); 272 | precache_sound ("zombie/z_miss.wav"); 273 | 274 | precache_model ("progs/player.mdl"); 275 | precache_model ("progs/eyes.mdl"); 276 | precache_model ("progs/h_player.mdl"); 277 | precache_model ("progs/gib1.mdl"); 278 | precache_model ("progs/gib2.mdl"); 279 | precache_model ("progs/gib3.mdl"); 280 | 281 | precache_model ("progs/s_bubble.spr"); // drowning bubbles 282 | precache_model ("progs/s_explod.spr"); // sprite explosion 283 | 284 | precache_model ("progs/v_axe.mdl"); 285 | precache_model ("progs/v_shot.mdl"); 286 | precache_model ("progs/v_nail.mdl"); 287 | precache_model ("progs/v_rock.mdl"); 288 | precache_model ("progs/v_shot2.mdl"); 289 | precache_model ("progs/v_nail2.mdl"); 290 | precache_model ("progs/v_rock2.mdl"); 291 | 292 | precache_model ("progs/bolt.mdl"); // for lightning gun 293 | precache_model ("progs/bolt2.mdl"); // for lightning gun 294 | precache_model ("progs/bolt3.mdl"); // for boss shock 295 | precache_model ("progs/lavaball.mdl"); // for testing 296 | 297 | precache_model ("progs/missile.mdl"); 298 | precache_model ("progs/grenade.mdl"); 299 | precache_model ("progs/spike.mdl"); 300 | precache_model ("progs/s_spike.mdl"); 301 | 302 | precache_model ("progs/backpack.mdl"); 303 | 304 | precache_model ("progs/zom_gib.mdl"); 305 | 306 | precache_model ("progs/v_light.mdl"); 307 | 308 | 309 | // 310 | // Setup light animation tables. 'a' is total darkness, 'z' is maxbright. 311 | // 312 | 313 | // 0 normal 314 | lightstyle(0, "m"); 315 | 316 | // 1 FLICKER (first variety) 317 | lightstyle(1, "mmnmmommommnonmmonqnmmo"); 318 | 319 | // 2 SLOW STRONG PULSE 320 | lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); 321 | 322 | // 3 CANDLE (first variety) 323 | lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); 324 | 325 | // 4 FAST STROBE 326 | lightstyle(4, "mamamamamama"); 327 | 328 | // 5 GENTLE PULSE 1 329 | lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj"); 330 | 331 | // 6 FLICKER (second variety) 332 | lightstyle(6, "nmonqnmomnmomomno"); 333 | 334 | // 7 CANDLE (second variety) 335 | lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm"); 336 | 337 | // 8 CANDLE (third variety) 338 | lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); 339 | 340 | // 9 SLOW STROBE (fourth variety) 341 | lightstyle(9, "aaaaaaaazzzzzzzz"); 342 | 343 | // 10 FLUORESCENT FLICKER 344 | lightstyle(10, "mmamammmmammamamaaamammma"); 345 | 346 | // 11 SLOW PULSE NOT FADE TO BLACK 347 | lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); 348 | 349 | // styles 32-62 are assigned by the light program for switchable lights 350 | 351 | // 63 testing 352 | lightstyle(63, "a"); 353 | }; 354 | 355 | //============================================================================ 356 | // This allows play_sound_triggered to keep playing after loading a game. 357 | // Thanks bmFbr 358 | //============================================================================ 359 | 360 | void() RestartLoopSounds_think = { 361 | sound(self, self.impulse, self.noise, self.volume, self.speed); 362 | }; 363 | 364 | void() RestartLoopSounds = { 365 | entity e; 366 | e = find(world, classname, "play_sound_triggered"); 367 | while (e) { 368 | 369 | if (e.spawnflags & 3 == 3) { // both "toggle" and "looped" need to be set 370 | if (e.state == 1) { 371 | e.nextthink = time + 0.1; 372 | e.think = RestartLoopSounds_think; 373 | } 374 | } 375 | 376 | e = find(e, classname, "play_sound_triggered"); 377 | } 378 | }; 379 | 380 | void() StartFrame = 381 | { 382 | teamplay = cvar("teamplay"); 383 | skill = cvar("skill"); 384 | framecount = framecount + 1; 385 | 386 | if (cleanUpClientStuff) { 387 | cleanUpClientStuff -= 1; 388 | 389 | RestartLoopSounds(); 390 | } 391 | else if (!gamestarted && framecount > 2) 392 | { 393 | if (framecount != 3) 394 | cleanUpClientStuff += 2; 395 | 396 | gamestarted = TRUE; 397 | } 398 | }; 399 | 400 | /* 401 | ============================================================================== 402 | 403 | BODY QUE 404 | 405 | ============================================================================== 406 | */ 407 | 408 | entity bodyque_head; 409 | 410 | void() bodyque = 411 | { // just here so spawn functions don't complain after the world 412 | // creates bodyques 413 | }; 414 | 415 | void() InitBodyQue = 416 | { 417 | bodyque_head = spawn(); 418 | bodyque_head.classname = "bodyque"; 419 | bodyque_head.owner = spawn(); 420 | bodyque_head.owner.classname = "bodyque"; 421 | bodyque_head.owner.owner = spawn(); 422 | bodyque_head.owner.owner.classname = "bodyque"; 423 | bodyque_head.owner.owner.owner = spawn(); 424 | bodyque_head.owner.owner.owner.classname = "bodyque"; 425 | bodyque_head.owner.owner.owner.owner = bodyque_head; 426 | }; 427 | 428 | 429 | // make a body que entry for the given ent so the ent can be 430 | // respawned elsewhere 431 | void(entity ent) CopyToBodyQue = 432 | { 433 | bodyque_head.angles = ent.angles; 434 | bodyque_head.model = ent.model; 435 | bodyque_head.modelindex = ent.modelindex; 436 | bodyque_head.frame = ent.frame; 437 | bodyque_head.colormap = ent.colormap; 438 | bodyque_head.movetype = ent.movetype; 439 | bodyque_head.velocity = ent.velocity; 440 | bodyque_head.flags = 0; 441 | setorigin (bodyque_head, ent.origin); 442 | setsize (bodyque_head, ent.mins, ent.maxs); 443 | bodyque_head = bodyque_head.owner; 444 | }; 445 | --------------------------------------------------------------------------------