diff --git a/base/def/weapon_bfg.def b/base/def/weapon_bfg.def
new file mode 100644
index 00000000..dcdda257
--- /dev/null
+++ b/base/def/weapon_bfg.def
@@ -0,0 +1,267 @@
+/***********************************************************************
+
+ weapon_bfg.def
+
+***********************************************************************/
+
+export fred {
+ // View Model BFG
+ options -prefix BFG_ -keep barrel LBarrel RBarrel guilight flash eject -parent guilight Body -parent flash Body -parent guilight Body -parent barrel Body -parent RBarrel Body -parent LBarrel Body -sourcedir models/weapons/bfg/cycles -destdir models/md5/weapons/bfg_view
+ mesh viewbfg.mb
+ anim raise.mb
+ anim idle.mb
+ anim fire.mb
+ anim fire.mb -range 1 140 -dest startfire
+ anim fire.mb -range 141 167 -dest bfgfire
+ anim empty_idle.mb
+ anim reload1.mb -dest reload
+ anim noammo.mb
+ anim lower.mb
+
+ // World Model BFG
+ options -prefix PLAYER_BFG_ -keep ATTACHER bfgbarrel -rename ATTACHER origin -parent bfgbarrel bfgbody -clearorigin
+ mesh models/characters/male_npc/cycles/tweakedplayermoves/bfg_idle.mb -dest models/md5/weapons/bfg_world/worldbfg
+ anim models/characters/male_npc/cycles/tweakedplayermoves/bfg_idle.mb -dest models/md5/weapons/bfg_world/bfg_idle
+}
+
+entityDef weapon_bfg {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "BFG"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/bfg/bfg_world.lwo"
+ "model_view" "viewmodel_bfg"
+ "model_world" "worldmodel_bfg"
+ "joint_attach" "BFG_ATTACHER"
+ "def_dropItem" "moveable_item_bfg"
+
+ "inv_name" "BFG-9000"
+ "inv_weapon" "weapon_bfg"
+ "inv_ammo_bfg" "4"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_8"
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".5"
+ "weaponAngleOffsetMax" "50"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "gui" "guis/weapons/bfg.gui"
+ "mtr_guiLightShader" "lights/viewWeaponGuiLight"
+
+ "def_overcharge_damage" "damage_bfg_overcharge"
+ "def_overcharge" "env_bfg_overcharge"
+ "snd_explode" "rocket_impact"
+
+ "smoke_muzzle" "BFG_barrel.ptr"
+
+ "weapon_class" "rvmWeaponBFG"
+ "def_projectile" "projectile_bfg"
+ "ammoType" "ammo_bfg"
+ "ammoRequired" "1"
+ "clipSize" "4"
+ "powerAmmo" "1"
+ "mtr_flashShader" "lights/bfg_flare"
+ "flashColor" "0.8 1 0.7"
+ "flashRadius" "32"
+ "silent_fire" "0"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+ "skin_invisible" "skins/bfg_invis"
+ //"snd_hum" "player_bfg_idle"
+ "spread" "0"
+}
+
+entityDef env_bfg_overcharge {
+ "spawnclass" "idAnimatedEntity"
+ "model" "bigexplosion.prt"
+}
+
+entityDef moveable_item_bfg {
+ "inherit" "weapon_bfg"
+ "spawnclass" "idMoveableItem"
+ "model" "models/weapons/bfg/bfg_world_rotated.lwo"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_bfg {
+ mesh models/md5/weapons/bfg_world/worldbfg.md5mesh
+
+ anim raise models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim idle models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim aim models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim fire models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim reload models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim noammo models/md5/weapons/bfg_world/bfg_idle.md5anim
+ anim putaway models/md5/weapons/bfg_world/bfg_idle.md5anim
+}
+
+model viewmodel_bfg {
+ mesh models/md5/weapons/bfg_view/viewbfg.md5mesh
+ offset ( -1 0 17 )
+
+ anim raise models/md5/weapons/bfg_view/raise.md5anim {
+ frame 1 sound_weapon player_bfg_raise
+
+ }
+ anim idle models/md5/weapons/bfg_view/idle.md5anim
+ anim fire_begin models/md5/weapons/bfg_view/startfire.md5anim {
+ frame 1 sound_weapon player_bfg_firebegin
+ }
+ anim fire models/md5/weapons/bfg_view/bfgfire.md5anim {
+ frame 1 sound_weapon player_bfg_fire
+ }
+ anim idle_empty models/md5/weapons/bfg_view/empty_idle.md5anim
+ anim putaway models/md5/weapons/bfg_view/lower.md5anim
+ anim reload models/md5/weapons/bfg_view/reload.md5anim {
+ frame 1 sound_weapon player_bfg_reload
+ }
+ anim noammo models/md5/weapons/bfg_view/noammo.md5anim {
+ frame 1 sound_weapon player_bfg_dryfire
+ }
+}
+
+entityDef damage_bfg {
+ "damage" "200"
+ "push" "1000"
+ "gib" "1"
+ "smoke_wound_flesh" "bloodwound.prt"
+}
+
+entityDef damage_bfgFreq {
+ "damage" "5"
+ "push" "0"
+ "gib" "1"
+ "snd_flesh" "bullet_impact_flesh"
+}
+
+entityDef damage_bfgSplash {
+ "damage" "100"
+ "radius" "150"
+ "knockback" "80"
+ "push" "7000"
+ "attackerDamageScale" "0.2"
+ "attackerPushScale" "0.2"
+ "gib" "1"
+}
+
+entityDef projectile_bfg {
+ "spawnclass" "idBFGProjectile"
+ "mins" "-10 -10 -10"
+ "maxs" "10 10 10"
+ "cone" "3"
+ "model" "models/particles/bfg_bolt/bfg_bolt.lwo"
+ "model_two" "models/particles/bfg_bolt/bfg_bolt2.lwo"
+
+ "no_contents" "1"
+
+ "def_damage" "damage_bfg"
+ "def_damageFreq" "damage_bfgFreq"
+ "def_splash_damage" "damage_bfgSplash"
+
+ "beam_WidthFly" "24"
+ "beam_WidthExplode" "128"
+ "skin_beam" "skins/duffybolt"
+
+ "snd_fly" "player_bfg_fly"
+ "snd_beam" "player_bfg_arc"
+ "remove_time" "7000"
+
+ "damageRadius" "2048"
+
+ "launchFromBarrel" "1"
+ "health" "5" // amount of damage projectile can take if damaged (0 means it can't be destroyed)
+ "velocity" "350 0 0" // how fast the projectile leaves the gun (or distance if fuse is 0)
+ "angular_velocity" "0 0 0" // how the projectile is rotating when it leaves the gun
+ "thrust" "0" // the rate of acceleration (always in the direction of the projectiles model)
+ "thrust_start" "0" // when to start accelerating
+ "thrust_end" "0" // when to stop accelerating
+ "linear_friction" "0" // "air" friction
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0" // how much speed a projectile retains when it bounces off of objects (coefficient of restitution). 0 means no bounce.
+ "mass" "50"
+ "gravity" "0" // how much gravity affects the trajectory. gravity direction is same as the entity that fired it.
+ "fuse" "4.5" // how long before the projectile is removed or self-detonates. Use 0 for beam weapons (velocity == distance).
+
+ "detonate_on_fuse" "1" // whether projectile should detonate when it's fuse runs out
+ "detonate_on_death" "1" // whether projectile should detonate when it's "killed" (health runs out)
+ "detonate_on_world" "1" // whether projectile should detonate when it hits an obstacle
+ "detonate_on_actor" "1" // whether projectile should detonate when it hits a character in the game
+
+ "impact_damage_effect" "1" // whether projectile causes blood splats from characters that bleed
+ "impact_gib" "1" // whether projectile can gib a character or not (for example: bullets don't gib, but rockets do)
+
+ "mtr_detonate" "textures/decals/bfg_wallmark"
+ "decal_size" "72"
+
+ "smoke_fly" "" // particle effect while in the air
+ "smoke_detonate" "" // particle effect when detonates
+ "smoke_residue" "" // particle effect while in the air
+ "smoke_bounce" "" // particle effect when bounces
+ "smoke_fuse" "" // particle effect when removed from fuse expiring
+
+ "model_detonate" "bfgExplosion.prt"
+ "debris_count" "6"
+ "def_debris" "debris_largeshrapnel"
+ "def_shrapnel" "debris_shrapnel"
+
+ "mtr_light_shader" "lights/baronflash"
+ "light_color" "1 1 1"
+ "light_radius" "160"
+ "light_offset" "0 0 -16"
+
+ "mtr_explode_light_shader" "muzzleflash"
+ "explode_light_color" "0 1 0"
+ "explode_light_radius" "300"
+ "explode_light_fadetime" "0.25"
+
+ // sound effect while in the air
+ "snd_explode" "player_bfg_explode"
+ "snd_explode2" "player_bfg_2explode"
+ "snd_explode3" "player_bfg_3explode"
+ "snd_explode4" "player_bfg_4explode"
+}
+
+// used by Campbell in cinematics
+entityDef projectile_bfg_cinematic {
+ "inherit" "projectile_bfg"
+ "no_contents" "1"
+ "velocity" "250 0 0"
+ "def_damage" "damage_bfg_cinematic"
+ "def_damageFreq" "damage_bfgFreq_cinematic"
+ "def_splash_damage" "damage_bfgSplash_cinematic"
+}
+
+entityDef damage_bfg_cinematic {
+ "inherit" "damage_bfg"
+ "ignore_player" "1"
+}
+
+entityDef damage_bfgFreq_cinematic {
+ "inherit" "damage_bfgFreq"
+ "ignore_player" "1"
+}
+
+entityDef damage_bfgSplash_cinematic {
+ "inherit" "damage_bfgSplash"
+ "ignore_player" "1"
+}
+
+entityDef damage_bfg_overcharge {
+ "damage" "500"
+ "radius" "256"
+ "push" "10000"
+ "knockback" "200"
+}
diff --git a/base/def/weapon_chaingun.def b/base/def/weapon_chaingun.def
new file mode 100644
index 00000000..a28a279d
--- /dev/null
+++ b/base/def/weapon_chaingun.def
@@ -0,0 +1,243 @@
+/***********************************************************************
+
+ weapon_chaingun.def
+
+***********************************************************************/
+
+export fred {
+ // viewmodel chaingun
+
+ options -prefix CHAINGUN_ -keep eject muzzle barrel flash guilight -parent eject chaingun -parent flash chaingun -parent guilight chaingun -parent barrel chaingun -rename barrel spinner -rename muzzle barrel -rename origin blah
+
+ mesh models/weapons/chaingun/cycles/idle.mb -dest models/md5/weapons/chaingun_view/viewchaingun
+ anim models/weapons/chaingun/cycles/raise.mb -dest models/md5/weapons/chaingun_view/raise
+ anim models/weapons/chaingun/cycles/idle.mb -dest models/md5/weapons/chaingun_view/idle
+ anim models/weapons/chaingun/cycles/fireloop.mb -dest models/md5/weapons/chaingun_view/fireloop
+ anim models/weapons/chaingun/cycles/reload.mb -dest models/md5/weapons/chaingun_view/reload
+ anim models/weapons/chaingun/cycles/noammo.mb -dest models/md5/weapons/chaingun_view/noammo
+ anim models/weapons/chaingun/cycles/lower.mb -dest models/md5/weapons/chaingun_view/lower
+ anim models/weapons/chaingun/cycles/windup.mb -dest models/md5/weapons/chaingun_view/windup
+ anim models/weapons/chaingun/cycles/winddown.mb -dest models/md5/weapons/chaingun_view/winddown
+
+ // world model chaingun
+ options -ignorescale -prefix PLAYER_CHAINGUN_ -keep ATTACHER barrel eject flash -parent barrel body -parent eject body -parent flash body -rename ATTACHER origin -clearorigin
+
+ mesh models/characters/player/mocap/chaingun_idle.mb -dest models/md5/weapons/chaingun_world/worldchaingun
+ anim models/characters/player/mocap/chaingun_idle.mb -range 1 80 -dest models/md5/weapons/chaingun_world/chaingun_idle
+ anim models/characters/male_npc/cycles/tweakedplayermoves/chaingun_fire.mb -range 16 30 -dest models/md5/weapons/chaingun_world/chaingun_fire
+}
+
+entityDef weapon_chaingun {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Chaingun"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/chaingun/w_chaingun.lwo"
+ "model_view" "viewmodel_chaingun"
+ "model_world" "worldmodel_chaingun"
+ "joint_attach" "CHAINGUN_ATTACHER"
+ "def_dropItem" "moveable_item_chaingun"
+
+ "inv_name" "Chaingun"
+ "inv_weapon" "weapon_chaingun"
+ "inv_ammo_belt" "30"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_11"
+
+ "gui" "guis/weapons/chaingun.gui"
+ "mtr_guiLightShader" "lights/viewWeaponGuiLight"
+
+ "weapon_class" "rvmWeaponChainGun"
+ "def_projectile" "projectile_chaingunbullet"
+ "ammoType" "ammo_belt"
+ "ammoRequired" "1"
+ "clipSize" "60"
+ "lowAmmo" "10"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "220"
+ "silent_fire" "0"
+ "muzzle_kick_time" ".1"
+ "muzzle_kick_maxtime" ".6"
+ "muzzle_kick_angles" "-4 -5 -10"
+ "muzzle_kick_offset" "1 1 2"
+ "recoilTime" "100"
+ "recoilAngles" "-1 0 0"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-25" // distance to offset weapon when talking to npcs or looking at guis
+
+ "weaponAngleOffsetAverages" "10"
+ "weaponAngleOffsetScale" ".5"
+ "weaponAngleOffsetMax" "25"
+ "weaponOffsetTime" "400"
+ "weaponOffsetScale" "0.005"
+
+ "smoke_wound_metal" "bulletricochet.prt"
+ "smoke_wound_ricochet" "bulletricochet.prt"
+
+ "smoke_muzzle" "cgmuzzlesmoke.prt"
+
+ "snd_acquire" "player_chaingun_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+ "snd_fire" "player_chaingun_fire"
+ "snd_winddown" "player_chaingun_winddown"
+ "snd_windup" "player_chaingun_windup"
+ "snd_lowammo" "player_machinegun_lowammo"
+ "snd_spin" "player_chaingun_motorloop"
+
+ "skin_invisible" "skins/chaingun_invis"
+
+ "def_ejectBrass" "debris_brass"
+ "ejectBrassDelay" "10"
+
+ "spread" "5"
+}
+
+entityDef moveable_item_chaingun {
+ "inherit" "weapon_chaingun"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_chaingun {
+ mesh models/md5/weapons/chaingun_world/worldchaingun.md5mesh
+
+ anim idle models/md5/weapons/chaingun_world/chaingun_idle.md5anim
+ anim fire models/md5/weapons/chaingun_world/chaingun_fire.md5anim
+ anim chaingun_fire models/md5/weapons/chaingun_world/chaingun_fire.md5anim
+}
+
+model viewmodel_chaingun {
+ mesh models/md5/weapons/chaingun_view/viewchaingun.md5mesh
+ offset ( 0.5 -2.5 -8.5 )
+
+ anim raise models/md5/weapons/chaingun_view/raise.md5anim {
+ frame 1 sound_body player_chaingun_acquire
+ }
+ anim idle1 models/md5/weapons/chaingun_view/idle.md5anim
+ anim windup models/md5/weapons/chaingun_view/windup.md5anim {
+ frame 1 sound_body3 player_chaingun_windup
+ //frame 10 sound_body3 player_chaingun_motorloop
+ }
+ /*
+ anim fire models/md5/weapons/chaingun_view/fireloop.md5anim {
+ frame 1 sound_weapon player_chaingun_fire
+ }
+ */
+ anim winddown models/md5/weapons/chaingun_view/winddown.md5anim {
+ frame 1 sound_body3 player_chaingun_winddown
+ }
+ anim putaway models/md5/weapons/chaingun_view/lower.md5anim
+ anim reload models/md5/weapons/chaingun_view/reload.md5anim {
+ frame 17 sound_body player_chaingun_reload
+ }
+ anim noammo models/md5/weapons/chaingun_view/noammo.md5anim
+}
+
+entityDef projectile_chaingunbullet {
+ "spawnclass" "idProjectile"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+ "cone" "3"
+ "scale" "0.1"
+ "noshadows" "1"
+ "tracers" "1"
+ "model_tracer" "models/particles/tracer/tracer.lwo"
+
+ "def_damage" "damage_bullet_chaingun"
+
+ "launchFromBarrel" "1"
+ "health" "0"
+ "velocity" "2000 0 0" // .38 Special 600 fps. Source: Petzal, David E. "How fast is a speeding bullet." Field and Stream. 97 (1992): 23
+ "angular_velocity" "0 0 0"
+ "thrust" "0"
+ "thrust_start" "0"
+ "thrust_end" "0"
+ "linear_friction" "0"
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6"
+ "mass" "8"
+ "push" "2000"
+ "gravity" "0"
+ "fuse" "4"
+
+ "detonate_on_fuse" "0"
+ "detonate_on_death" "0"
+ "detonate_on_world" "1"
+ "detonate_on_actor" "1"
+
+ "impact_damage_effect" "1"
+ "impact_gib" "1"
+
+ "mtr_detonate" "textures/decals/bulleth02"
+ "mtr_detonate_glass" "textures/decals/testbulletglass1"
+ "decal_size" "10"
+
+ "model_smokespark" "cgsmokeandspark.prt"
+ "model_ricochet" "bulletricochet.prt"
+
+ "smoke_wound_flesh" "burstysquirt.prt"
+ "smoke_detonate" "smokeandspark.smoke"
+ "smoke_residue" "" //particles/chaingun_residue.particle"
+ "smoke_bounce" "" //particles/chaingun_bounce.particle"
+ "smoke_fuse" ""
+
+ "mtr_light_shader" ""
+ "light_color" "0 0 0"
+ "light_radius" "0"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" ""
+ "explode_light_color" "0 0 0"
+ "explode_light_radius" "0"
+ "explode_light_fadetime" "0"
+
+ "snd_flesh" "bullet_impact_flesh"
+ "snd_metal" "bullet_impact_metal"
+ "snd_stone" "bullet_impact_stone"
+ "snd_wood" "bullet_impact_wood"
+ "snd_cardboard" "bullet_impact_cardboard"
+ "snd_glass" "bullet_impact_glass"
+ "snd_liquid" "bullet_impact_liquid"
+ "snd_plastic" "small_plastic"
+ "snd_ricochet" "bullet_ricochet"
+// "snd_impact" "" // we need a much smaller impact sound for the chaingun bullet "bullet_impact_metal"
+ "snd_tracer" "bullet_flight"
+}
+
+entityDef damage_bullet_chaingun {
+ "damage" "20"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "1"
+
+ "gib" "1"
+ "smoke_wound_flesh" "bloodwound.ptr"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_wound_metal" "textures/decals/hurtmetal"
+ "mtr_wound_ricochet" "textures/decals/hurtmetal"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_splat_flesh3" "textures/decals/dsplat7"
+ "mtr_splat_flesh4" "textures/decals/dsplat11"
+
+ "kick_time" "100"
+ //"kick_time" "10"
+ "kick_amplitude" "0.1"
+
+ //"dv_time" "100"
+
+}
diff --git a/base/def/weapon_chainsaw.def b/base/def/weapon_chainsaw.def
new file mode 100644
index 00000000..23f3401c
--- /dev/null
+++ b/base/def/weapon_chainsaw.def
@@ -0,0 +1,193 @@
+/***********************************************************************
+
+ weapon_chainsaw.def
+
+***********************************************************************/
+
+export fred {
+ // view model chainsaw
+ options -prefix CHAINSAW_ -keep barrel -parent boo chainsaw -parent barrel chainsaw -rename origin blah
+ mesh models/weapons/chainsaw/cycles/idle.mb -dest models/md5/weapons/chainsaw_view/viewchainsaw
+ anim models/weapons/chainsaw/cycles/idle2.mb -dest models/md5/weapons/chainsaw_view/idle2
+ anim models/weapons/chainsaw/cycles/lower.mb -dest models/md5/weapons/chainsaw_view/lower
+
+ anim models/weapons/chainsaw/cycles/melee4.mb -dest models/md5/weapons/chainsaw_view/melee
+ anim models/weapons/chainsaw/cycles/melee4.mb -range 1 10 -dest models/md5/weapons/chainsaw_view/melee_start
+ anim models/weapons/chainsaw/cycles/melee4.mb -range 11 16 -dest models/md5/weapons/chainsaw_view/melee_loop
+ anim models/weapons/chainsaw/cycles/melee4.mb -range 17 38 -dest models/md5/weapons/chainsaw_view/melee_end
+ anim models/weapons/chainsaw/cycles/wallkick.mb -range 17 40 -dest models/md5/weapons/chainsaw_view/wallkick
+
+ anim models/weapons/chainsaw/cycles/newraise.mb -dest models/md5/weapons/chainsaw_view/newraise
+
+ // world model chainsaw
+ options -prefix PLAYER_CHAINSAW_ -keep ATTACHER -rename ATTACHER origin -clearorigin
+ addoptions -skipmesh PLAYER_chainsawcordmesh
+ addoptions -skipmesh PLAYER_chainsawblademesh
+ addoptions -skipmesh PLAYER_chainsawhandlemesh
+ addoptions -skipmesh PLAYER_chainsawbodymesh
+ addoptions -skipmesh PLAYER_chainsawstartermesh
+
+ mesh models/characters/player/mocap/chainsaw_idle.mb -dest models/md5/weapons/chainsaw_world/worldchainsaw
+ anim models/characters/player/mocap/chainsaw_idle.mb -dest models/md5/weapons/chainsaw_world/chainsaw_idle
+ anim models/characters/player/mocap/chainsaw_raise.mb -range 43 114 -dest models/md5/weapons/chainsaw_world/chainsaw_raise1
+ anim models/characters/player/mocap/chainsaw_raise.mb -range 43 144 -dest models/md5/weapons/chainsaw_world/chainsaw_raise2
+ anim models/characters/player/mocap/chainsaw_raise.mb -range 43 180 -dest models/md5/weapons/chainsaw_world/chainsaw_raise3
+}
+
+entityDef weapon_chainsaw {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Chainsaw"
+ "editor_rotatable" "1"
+
+ "size" "32 32 32"
+ "model" "models/weapons/chainsaw/w_chainsaw.lwo"
+ "model_view" "viewmodel_chainsaw"
+ "model_world" "worldmodel_chainsaw"
+ "joint_attach" "CHAINSAW_ATTACHER"
+ "def_dropItem" "moveable_item_chainsaw"
+
+ "inv_name" "#str_00100205"
+ "inv_weapon" "weapon_chainsaw"
+ "inv_item" "5"
+
+ "inv_icon" "guis/assets/hud/wpn_9"
+ "inv_desc" "#str_02541"
+ "display_name" "#str_02481"
+ "pdaIcon" "guis/assets/hud/icons/chainsaw_new.tga"
+ "hudIcon" "guis/assets/hud/icons/chainsaww.tga"
+
+ "weapon_class" "rvmWeaponChainsaw"
+ "def_melee" "damage_chainsaw"
+ "melee_distance" "64"
+ "ammoType" ""
+ "ammoRequired" "0"
+ "clipSize" "0"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "recoilTime" "0"
+ "recoilAngles" "0 0 0"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-20" // distance to offset weapon when talking to npcs or looking at guis
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".5"
+ "weaponAngleOffsetMax" "50"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ "impact_damage_effect" "1"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+ "snd_hum" "player_chainsaw_idle"
+
+ "smoke_muzzle" "chainsaw_smoke.prt"
+ "continuousSmoke" "1"
+
+ "smoke_strike" "chainsawstrike.prt"
+ "mtr_strike" "textures/decals/chainsawh02"
+
+ "snd_startattack" "player_chainsaw_startAttack"
+ "snd_attack" "player_chainsaw_attack"
+ "snd_stopattack" "player_chainsaw_stopAttack"
+ "snd_cspull" "monster_zombie_chainsaw_pull"
+ "snd_csrelease" "monster_zombie_chainsaw_release"
+ "snd_csattack" "monster_zombie_chainsaw_attack1"
+ "snd_csidle" "monster_zombie_chainsaw_idle"
+ "snd_putaway" "player_chainsaw_putaway"
+
+ "skin_invisible" "skins/chainsaw_invis"
+
+ "controllerShakeHighMag" "1.0"
+ "controllerShakeHighTime" "100"
+ "controllerShakeLowMag" "0.9"
+ "controllerShakeLowTime" "250"
+
+ "controllerConstantShakeHighMag" "0.2"
+ "controllerConstantShakeHighTime" "100"
+ "controllerConstantShakeLowMag" "0.05"
+ "controllerConstantShakeLowTime" "250"
+}
+
+entityDef moveable_item_chainsaw {
+ "inherit" "weapon_chainsaw"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_chainsaw {
+ mesh models/md5/weapons/chainsaw_world/worldchainsaw.md5mesh
+
+ anim idle models/md5/weapons/chainsaw_world/chainsaw_idle.md5anim
+ anim raise models/md5/weapons/chainsaw_world/chainsaw_idle.md5anim
+}
+
+model viewmodel_chainsaw {
+ mesh models/md5/weapons/chainsaw_view/viewchainsaw.md5mesh
+ offset ( -4 3 2 )//( 5 1 1 )
+
+ anim idle models/md5/weapons/chainsaw_view/idle2.md5anim
+
+ anim melee_start models/md5/weapons/chainsaw_view/melee_start.md5anim
+
+ anim melee_loop models/md5/weapons/chainsaw_view/melee_loop.md5anim
+
+ anim melee_end models/md5/weapons/chainsaw_view/melee_end.md5anim
+
+ anim wallkick models/md5/weapons/chainsaw_view/wallkick.md5anim
+
+
+
+ anim putaway models/md5/weapons/chainsaw_view/lower.md5anim {
+ frame 1 sound_weapon player_chainsaw_putaway
+ }
+ anim raise1 models/md5/weapons/chainsaw_view/newraise.md5anim {
+ frame 3 sound_voice2 player_chainsaw_pull_zip
+ frame 13 sound_weapon player_chainsaw_pull
+ frame 22 sound_body2 player_chainsaw_release
+ }
+
+}
+
+entityDef damage_chainsaw {
+ "damage" "50"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "40"
+ "push" "2000"
+
+ "gib" "1"
+ "smoke_wound_flesh" "chainsawwound.prt"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_wound_metal" "textures/decals/chainsawh02"
+ "mtr_wound_glass" "textures/decals/chainsawh02"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+
+ "snd_hit" "player_chainsaw_hit_flesh" // TEMP
+ "snd_metal" "player_chainsaw_hit_metal"
+ "snd_flesh" "player_chainsaw_hit_flesh"
+ "snd_stone" "player_chainsaw_hit_metal"
+ "snd_wood" "player_chainsaw_hit_metal"
+ "snd_cardboard" "player_chainsaw_hit_metal"
+ "snd_glass" "player_chainsaw_hit_metal"
+ "snd_liquid" "player_chainsaw_hit_metal"
+ "snd_plastic" "player_chainsaw_hit_metal"
+}
diff --git a/base/def/weapon_fists.def b/base/def/weapon_fists.def
new file mode 100644
index 00000000..dbc65499
--- /dev/null
+++ b/base/def/weapon_fists.def
@@ -0,0 +1,108 @@
+/***********************************************************************
+
+ weapon_fists.def
+
+***********************************************************************/
+
+export fred {
+ // view model fists
+ options -prefix FIST_
+ mesh models/weapons/berserk/cycles/idle.mb -dest models/md5/weapons/fists_view/fists
+ anim models/weapons/berserk/cycles/idle.mb -rotate 180 -dest models/md5/weapons/fists_view/idle
+ anim models/weapons/berserk/cycles/berserk_idle.mb -rotate 180 -dest models/md5/weapons/fists_view/berserk_idle
+ anim models/weapons/berserk/cycles/berserk_punch1.mb -rotate 180 -dest models/md5/weapons/fists_view/berserk_punch1
+ anim models/weapons/berserk/cycles/berserk_punch2.mb -rotate 180 -dest models/md5/weapons/fists_view/berserk_punch2
+ anim models/weapons/berserk/cycles/berserk_punch3.mb -rotate 180 -dest models/md5/weapons/fists_view/berserk_punch3
+ anim models/weapons/berserk/cycles/berserk_punch4.mb -rotate 180 -dest models/md5/weapons/fists_view/berserk_punch4
+
+
+ anim models/weapons/berserk/cycles/punch1.mb -range 30 53 -rotate 180 -dest models/md5/weapons/fists_view/punch1
+ anim models/weapons/berserk/cycles/punch2.mb -range 30 53 -rotate 180 -dest models/md5/weapons/fists_view/punch2
+ anim models/weapons/berserk/cycles/punch2.mb -range 133 160 -rotate 180 -dest models/md5/weapons/fists_view/punch3
+ anim models/weapons/berserk/cycles/punch2.mb -range 186 193 -rotate 180 -dest models/md5/weapons/fists_view/punch4
+ anim models/weapons/berserk/cycles/punch2.mb -range 193 213 -rotate 180 -dest models/md5/weapons/fists_view/punch5
+ anim models/weapons/berserk/cycles/punch2.mb -range 20 30 -rotate 180 -dest models/md5/weapons/fists_view/raise
+ anim models/weapons/berserk/cycles/punch2.mb -range 213 218 -rotate 180 -dest models/md5/weapons/fists_view/lower
+}
+
+entityDef weapon_fists {
+ "model_view" "viewmodel_fists"
+
+ "inv_name" "Fists"
+ "inv_weapon" "weapon_fists"
+ "icon" "guis/assets/hud/wpn_12"
+
+ "weapon_class" "rvmWeaponFist"
+ "def_melee" "damage_fists"
+ "melee_distance" "48"
+ "ammoType" ""
+ "ammoRequired" "0"
+ "clipSize" "0"
+ "mtr_flashShader" ""
+ "flashColor" ""
+ "flashRadius" "0"
+ "silent_fire" "1"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "impact_damage_effect" "1"
+
+ "snd_acquire" "guisounds_weapon"
+ "skin_invisible" "skins/fists_invis"
+
+ "stealing" "1" // this weapon steals other player weapon
+}
+
+model viewmodel_fists {
+ mesh models/md5/weapons/fists_view/fists.md5mesh
+ offset ( -10 0 -6.5 )
+
+ anim raise models/md5/weapons/fists_view/raise.md5anim {
+ frame 1 sound_weapon fist_raise
+ }
+ anim idle models/md5/weapons/fists_view/idle.md5anim
+ anim berserk_idle models/md5/weapons/fists_view/berserk_idle.md5anim
+ anim aim models/md5/weapons/fists_view/idle.md5anim
+ anim punch_left1 models/md5/weapons/fists_view/berserk_punch1.md5anim
+ anim punch_left2 models/md5/weapons/fists_view/berserk_punch3.md5anim
+ anim punch_right1 models/md5/weapons/fists_view/berserk_punch2.md5anim
+ anim punch_right2 models/md5/weapons/fists_view/berserk_punch4.md5anim
+ anim putaway models/md5/weapons/fists_view/lower.md5anim
+}
+
+entityDef damage_fists {
+ "damage" "20"
+ "kickDir" "-1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "20"
+ "push" "20000"
+
+ "gib" "1"
+ "smoke_wound_flesh" "burstysquirt.prt"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+
+ "snd_metal" "fist_impact"
+ "snd_ricochet" "fist_impact"
+ "snd_flesh" "fist_impact_flesh"
+ "snd_stone" "fist_impact"
+ "snd_wood" "fist_impact"
+ "snd_cardboard" "fist_impact"
+ "snd_glass" "fist_impact"
+ "snd_liquid" "fist_impact"
+ "snd_plastic" "fist_impact"
+
+ "snd_hit" "fist_impact"
+ "snd_miss" "fist_whoosh"
+ "snd_hit_berserk" "fist_gorepunch"
+}
diff --git a/base/def/weapon_flashlight.def b/base/def/weapon_flashlight.def
new file mode 100644
index 00000000..e67fd11b
--- /dev/null
+++ b/base/def/weapon_flashlight.def
@@ -0,0 +1,268 @@
+/***********************************************************************
+
+ weapon_flashlight.def
+
+***********************************************************************/
+
+export fred {
+ // Flashlight
+ options -prefix FLASHLIGHT_ -keep flash -parent flash Flashlight
+ mesh models/items/flashlight/cycles/allanims.mb -dest models/md5/items/flashlight_view/viewflashlight
+ anim models/items/flashlight/cycles/allanims.mb -range 1 90 -dest models/md5/items/flashlight_view/idle
+ anim models/items/flashlight/cycles/allanims.mb -range 91 102 -dest models/md5/items/flashlight_view/pushbutton
+ anim models/items/flashlight/cycles/allanims.mb -range 112 135 -dest models/md5/items/flashlight_view/swing1
+ anim models/items/flashlight/cycles/allanims.mb -range 137 162 -dest models/md5/items/flashlight_view/swing2
+ anim models/items/flashlight/cycles/allanims.mb -range 165 170 -dest models/md5/items/flashlight_view/lower
+ anim models/items/flashlight/cycles/allanims.mb -range 176 190 -dest models/md5/items/flashlight_view/raise
+
+ // world model flashlight
+ options -prefix PLAYER_FL_ -keep ATTACHER flash -parent flash Flashlight -rename ATTACHER origin -clearorigin
+ mesh models/characters/player/mocap/flashlight_idle.mb -dest models/md5/items/flashlight_world/worldflashlight
+ anim models/characters/player/mocap/flashlight_idle.mb -dest models/md5/items/flashlight_world/flashlight_idle
+}
+
+entityDef weapon_flashlight {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Flashlight"
+ "editor_rotatable" "1"
+ "model" "models/items/flashlight/flashlight2_world.lwo"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model_view" "viewmodel_flashlight"
+ "model_world" "worldmodel_flashlight"
+ "joint_attach" "FL_ATTACHER"
+
+ "inv_name" "Flashlight"
+ "inv_weapon" "weapon_flashlight"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_10"
+
+ "impact_damage_effect" "1"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "weapon_class" "rvmWeaponFlashlight"
+ "def_melee" "damage_flashlight"
+ "melee_distance" "48"
+ "ammoType" ""
+ "ammoRequired" "0"
+ "clipSize" "0"
+ "mtr_flashShader" "lights/flashlight5"
+ "flashColor" "1 1 1"
+ "flashTime" "0.016"
+ "flashRadius" "400"
+ "silent_fire" "1"
+ "flashAngle" "22.0"
+ "flashTarget" "0 0 1280"
+ "flashUp" "0 640 0"
+ "flashRight" "-640 0 0"
+ "flashPointLight" "0"
+
+ "snd_acquire" "flashlight_pickup"
+
+ // MP
+ "skin_on" "skins/models/weapons/flashlight_on"
+ "skin_on_invis" "skins/models/weapons/flashlight_on_invis"
+ "skin_off" "skins/models/weapons/flashlight_off"
+ "skin_off_invis" "skins/models/weapons/flashlight_off_invis"
+}
+
+entityDef moveable_item_flashlight {
+ "inherit" "weapon_flashlight"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_flashlight {
+ mesh models/md5/items/flashlight_world/worldflashlight.md5mesh
+
+ anim raise models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim idle models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim aim models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim fire models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim reload models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim noammo models/md5/items/flashlight_world/flashlight_idle.md5anim
+ anim putaway models/md5/items/flashlight_world/flashlight_idle.md5anim
+}
+
+model viewmodel_flashlight {
+ mesh models/md5/items/flashlight_view/viewflashlight.md5mesh
+ offset ( -7 2 16 )
+
+ anim raise models/md5/items/flashlight_view/raise.md5anim {
+ frame 1 sound_weapon flashlight_raise
+ }
+ anim idle models/md5/items/flashlight_view/idle.md5anim
+ anim fire1 models/md5/items/flashlight_view/swing1.md5anim
+ anim fire2 models/md5/items/flashlight_view/swing2.md5anim
+ anim putaway models/md5/items/flashlight_view/lower.md5anim
+ anim reload models/md5/items/flashlight_view/pushbutton.md5anim
+ anim noammo models/md5/items/flashlight_view/idle.md5anim
+}
+
+entityDef damage_flashlight {
+ "damage" "40" //may31 -Tim may be too high
+ "kickDir" "-1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "gib" "1"
+ "smoke_wound_flesh" "burstysquirt.prt"
+
+ "knockback" "20"
+ "push" "15000"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+
+ "snd_flesh" "flashlight_impact"
+ "snd_metal" "monster_zombie_fat_wrench"
+ "snd_ricochet" "monster_zombie_fat_wrench" // need this
+ "snd_stone" "monster_zombie_fat_wrench"
+ "snd_wood" "flashlight_impact"
+ "snd_cardboard" "flashlight_impact"
+ "snd_glass" "monster_zombie_fat_wrench"
+// "snd_liquid" "bullet_impact_liquid"
+ "snd_plastic" "flashlight_impact"
+
+ "snd_hit" "flashlight_impact" // TEMP
+ "snd_miss" "fist_whoosh" // TEMP
+ "snd_hit_berserk" "fist_gorepunch"
+}
+
+material lights/flashlight5broken {
+ lightFalloffImage makeIntensity( lights/flashoff.tga )
+ {
+ map lights/flashlight5broken.tga
+ zeroClamp
+ //rotate time * -.01
+ //colored
+ rgb xray02table[ time * .5 ] * parm3
+ }
+}
+
+material lights/flashlight5 {
+ lightFalloffImage makeIntensity( lights/flashoff.tga )
+ {
+ map lights/flashlight5.tga
+ zeroClamp
+ //rotate time * -.01
+ //colored
+ rgb bathroomtable[ time * .05 ] * parm3
+ }
+}
+
+material lights/flashlight5gore {
+ lightFalloffImage makeIntensity( lights/flashoff.tga )
+ {
+ map lights/flashlight5gore.tga
+ zeroClamp
+ //colored
+ rgb bathroomtable[ time * .05 ] * parm3
+ }
+}
+
+material models/items/flashlight/flare {
+ translucent
+ deform sprite
+ {
+ blend add
+ map models/items/flashlight/flare.tga
+ }
+}
+
+material models/items/flashlight/beam1 {
+ translucent
+ deform tube
+ {
+ maskcolor
+ map makealpha(models/items/flashlight/beam1.tga)
+ // scroll time * 3 , time * 0
+ forcehighquality
+ }
+ {
+ blend gl_dst_alpha, gl_one
+ maskalpha
+ map models/items/flashlight/dust
+ scale 4 , 4
+ rotate time * -.02
+ scroll time * .001 , time * .001
+ }
+}
+
+material models/items/flashlight/flare2 {
+ translucent
+ deform sprite
+ {
+ blend add
+ map models/items/flashlight/flare2.tga
+ }
+}
+
+material models/items/flashlight/beam2 {
+ translucent
+ deform tube
+ {
+ maskcolor
+ map makealpha(models/items/flashlight/beam1.tga)
+ // scroll time * 3 , time * 0
+ forcehighquality
+
+
+ }
+ {
+ blend gl_dst_alpha, gl_one
+ maskalpha
+ map models/items/flashlight/dust
+ scale 4 , 4
+ rotate time * -.02
+ scroll time * .001 , time * .001
+ }
+}
+
+material models/items/flashlight/bulb {
+ translucent
+ {
+ blend add
+ map models/items/flashlight/bulb.tga
+ }
+}
+
+skin skins/models/weapons/flashlight_off {
+ models/items/flashlight/flare textures/common/nodraw
+ models/items/flashlight/beam1 textures/common/nodraw
+ models/items/flashlight/flare2 textures/common/nodraw
+ models/items/flashlight/beam2 textures/common/nodraw
+ models/items/flashlight/bulb textures/common/nodraw
+}
+
+skin skins/models/weapons/flashlight_off_invis {
+ models/items/flashlight/flashlight2 models/items/flashlight/flashlight2_invis
+ models/characters/player/arm2 models/characters/player/arm2_invis
+ models/items/flashlight/flare textures/common/nodraw
+ models/items/flashlight/beam1 textures/common/nodraw
+ models/items/flashlight/flare2 textures/common/nodraw
+ models/items/flashlight/beam2 textures/common/nodraw
+ models/items/flashlight/bulb textures/common/nodraw
+}
+
+skin skins/models/weapons/flashlight_on {
+}
+
+skin skins/models/weapons/flashlight_on_invis {
+ models/items/flashlight/flashlight2 models/items/flashlight/flashlight2_invis
+ models/characters/player/arm2 models/characters/player/arm2_invis
+}
+
+
diff --git a/base/def/weapon_grabber.def b/base/def/weapon_grabber.def
new file mode 100644
index 00000000..ce197927
--- /dev/null
+++ b/base/def/weapon_grabber.def
@@ -0,0 +1,292 @@
+export jay8 {
+ // view model grabber
+ options -prefix PISTOL_ -keep sidelight grabber_root beam guilight light_upper light_lower particle_upper particle_lower smokeparticle -parent sidelight grabber_root -parent smokeparticle grabber_cylinder01 -parent particle_upper grabber_root -parent particle_lower grabber_root -parent light_upper grabber_root -parent light_lower grabber_root -parent guilight grabber_root -rename origin blah -sourcedir models/weapons/grabber/cycles -destdir models/md5/weapons/grabber_view
+
+ mesh idleopen.ma
+ anim idleopen.ma
+ anim idleclosed.ma
+ anim raise.ma
+ anim open.ma
+ anim close.ma
+ anim fire.ma
+ anim put_away.ma
+}
+
+export jaygrabworld {
+
+ // world model grabber
+ options -prefix PLAYER_GRABBER_ -keep ATTACHER particle_upper -parent particle_upper body -rename ATTACHER origin -clearorigin
+
+
+ mesh models/characters/player_character/cycles/grabber_w_idle.mb -dest models/md5/weapons/grabber_world/worldgrabber
+ anim models/characters/player_character/cycles/grabber_w_idle.mb -range 0 0 -dest models/md5/weapons/grabber_world/grabber_idle
+}
+
+model worldmodel_grabber {
+ mesh models/md5/weapons/grabber_world/worldgrabber.md5mesh
+
+ anim raise models/md5/weapons/grabber_world/grabber_idle.md5anim
+ anim idle models/md5/weapons/grabber_world/grabber_idle.md5anim
+ anim aim models/md5/weapons/grabber_world/grabber_idle.md5anim
+ anim fire1 models/md5/weapons/grabber_world/grabber_idle.md5anim
+ anim reload_start models/md5/weapons/grabber_world/grabber_idle.md5anim
+ anim putaway models/md5/weapons/grabber_world/grabber_idle.md5anim
+}
+
+model viewmodel_grabber {
+ mesh models/md5/weapons/grabber_view/idleopen.md5mesh
+ offset ( -1 -1 0 )
+
+ anim raise models/md5/weapons/grabber_view/raise.md5anim {
+ frame 1 sound_weapon grabber_present
+ }
+ anim idleopen models/md5/weapons/grabber_view/idleopen.md5anim
+ anim idleclosed models/md5/weapons/grabber_view/idleclosed.md5anim
+ anim idle models/md5/weapons/grabber_view/idleclosed.md5anim
+ anim open models/md5/weapons/grabber_view/open.md5anim
+ anim close models/md5/weapons/grabber_view/close.md5anim
+ anim fire models/md5/weapons/grabber_view/fire.md5anim
+ anim putaway models/md5/weapons/grabber_view/put_away.md5anim
+}
+
+entityDef weapon_grabber {
+
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_rotatable" "1"
+ "editor_usage" "Grabber"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/grabber/grabber_world.ase"
+ "model_view" "viewmodel_grabber"
+ "model_world" "worldmodel_grabber"
+ "joint_attach" "SHOTGUN_ATTACHER"
+
+ "inv_name" "#str_00100207"
+ "inv_weapon" "weapon_grabber"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_10"
+ "inv_desc" "#str_00100210"
+ "display_name" "#str_00100207"
+ "hudIcon" "guis/assets/hud/icons/grabberw.tga"
+
+ "gui" "guis/weapons/grabber.gui"
+
+ "smoke_muzzle" "grabbersmoke.prt"
+ "smoke_joint" "smokeparticle"
+
+ "weapon_class" "rvmWeaponGrabber"
+ "def_melee" "damage_flashlight"
+ "melee_distance" "32"
+ "ammoType" ""
+ "ammoRequired" "0"
+ "clipSize" "0"
+ "mtr_flashShader" "lights/flashlight5"
+ "flashColor" "1 1 1"
+ "flashTime" "0.016"
+ "flashRadius" "400"
+ "silent_fire" "1"
+ "flashAngle" "22.0"
+ "flashTarget" "0 0 1280"
+ "flashUp" "0 640 0"
+ "flashRight" "-640 0 0"
+ "flashPointLight" "0"
+
+ "snd_cangrab" "player_grabber_cangrab"
+ "snd_acquire" "guisounds_weapon"
+ "snd_warning" "player_grabber_warning"
+ "snd_hum" "grabber_idle"
+ "snd_fireloop" "grabber_fireloop"
+ "snd_stopfire" "grabber_stopfire"
+ "snd_startfire" "grabber_startfire"
+ "snd_release" "grabber_release" // -Xian
+ "snd_electroloop" "grabber_electro"
+ "snd_mainfire" "grabber_mainfire"
+ "snd_maindrop" "grabber_maindrop"
+
+ "skin_flashon" "skins/models/weapons/flashlight_on.skin"
+
+ "weapon_particle1" "barrel_upper"
+ "barrel_upper_joint" "particle_upper"
+ "barrel_upper_particle" "grabber_upper.prt"
+ "barrel_upper_smoke" "0"
+
+ "weapon_light1" "light_barrel_upper"
+ "light_barrel_upper_joint" "light_upper"
+ "light_barrel_upper_shader" "lights/grabberLight"
+ "light_barrel_upper_radius" "50"
+
+ "weapon_light2" "light_barrel_lower"
+ "light_barrel_lower_joint" "light_lower"
+ "light_barrel_lower_shader" "lights/grabberLight"
+ "light_barrel_lower_radius" "50"
+
+ "weapon_light3" "light_side"
+ "light_side_joint" "sidelight"
+ "light_side_shader" "lights/grabberLight2"
+ "light_side_radius" "50"
+
+ "weapon_light4" "gLightBlue"
+ "gLightBlue_joint" "guilight"
+ "gLightBlue_shader" "lights/grabberguilightblue"
+ "gLightBlue_radius" "3"
+
+ "weapon_light5" "gLightYellow"
+ "gLightYellow_joint" "guilight"
+ "gLightYellow_shader" "lights/grabberguilightyellow"
+ "gLightYellow_radius" "3"
+
+ "weapon_light6" "gLightRed"
+ "gLightRed_joint" "guilight"
+ "gLightRed_shader" "lights/grabberguilightred"
+ "gLightRed_radius" "3"
+
+ // Precache effects
+ "model_barrelburn" "barrelfire.prt"
+ "model_precache1" "grabber_upper.prt"
+ "mtr_precache2" "lights/grabberLight"
+ "mtr_precache3" "lights/grabberLight2"
+ "mtr_precache4" "lights/grabberguilightblue"
+ "mtr_precache5" "lights/grabberguilightyellow"
+ "mtr_precache6" "lights/grabberguilightred"
+
+ "skin_invisible" "skins/grabber_invis"
+}
+
+
+// ======================================================
+//
+// Damage Defs for caught projectiles
+//
+// ======================================================
+entityDef damage_impfireball_catch {
+ "damage" "150"
+ "kickDir" "1 0 0"
+ "push" "500"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+}
+
+entityDef damage_fireball_hellknight_catch {
+ "damage" "250"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+}
+
+entityDef damage_RevRocket_catch {
+ "damage" "250"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ //"smoke_wound_flesh" "bloodwound.smoke"
+ "snd_flesh" "bullet_impact_flesh"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+}
+
+entityDef damage_vulgar_fireball_catch {
+ "damage" "150"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+}
+
+entityDef damage_mancubusblast_catch {
+ "damage" "400"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+ "snd_flesh" "bullet_impact_flesh"
+}
+
+entityDef damage_cacodemon_fireball_catch {
+ "damage" "200"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+}
+
+entityDef damage_fireball_invul_catch {
+ "damage" "375"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+ "smoke_wound_flesh" "bloodwound.smoke"
+}
+
diff --git a/base/def/weapon_handgrenade.def b/base/def/weapon_handgrenade.def
new file mode 100644
index 00000000..d6e83ac2
--- /dev/null
+++ b/base/def/weapon_handgrenade.def
@@ -0,0 +1,233 @@
+/***********************************************************************
+
+ weapon_handgrenade.def
+
+***********************************************************************/
+
+export fred {
+ // View Model Grenade
+ options -prefix NADE_ -keep barrel -parent barrel Rhand -sourcedir models/weapons/grenades/cycles -destdir models/md5/weapons/grenade_view
+ mesh idle1.mb -dest viewgrenade
+ anim idle1.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_idle
+ anim raise.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_raise
+ anim lower.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_lower
+ //anim fire1.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_fire1
+ anim fire2.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_fire2
+ anim start.mb -xyzprecision 0.01 -quatprecision 0.00006 -range 20 130 -dest grenade_start
+ anim start.mb -xyzprecision 0.01 -quatprecision 0.00006 -range 152 180 -dest grenade_throw
+ //anim models/weapons/grenades/cycles/fire2.mb -xyzprecision 0.01 -quatprecision 0.00006 -range 99 132 -dest throw
+ //anim models/weapons/grenades/cycles/fire3.mb -xyzprecision 0.01 -quatprecision 0.00006 -range 98 132 -dest grenade_lowthrow
+ anim fastthrow.mb -xyzprecision 0.01 -quatprecision 0.00006 -dest grenade_fastthrow
+
+
+ // World Model Grenade
+ options -prefix PLAYER_NADE_ -keep ATTACHER barrel -parent barrel gbody -rename ATTACHER origin -clearorigin -sourcedir models/characters/player/mocap -destdir models/md5/weapons/grenade_world
+ mesh grenade_idle.mb -dest worldgrenade
+ anim grenade_idle.mb -dest grenade_idle
+ anim grenade_throw.mb -dest grenade_throw
+}
+
+entityDef weapon_handgrenade {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Hand Grenade"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/items/grenade_ammo/grenade.lwo"
+ "model_view" "viewmodel_grenade"
+ "model_world" "worldmodel_handgrenade"
+ "joint_attach" "NADE_ATTACHER"
+ "def_dropItem" "moveable_item_grenades"
+
+ "modelindex" "MODELINDEX_GRENADELAUNCHER"
+
+ "inv_name" "#str_07013"
+ "inv_weapon" "weapon_handgrenade"
+ "inv_ammo_grenades" "5"
+ "inv_item" "5"
+ "inv_desc" "#str_02537"
+ "display_name" "#str_02478"
+
+ "icon" "guis/assets/hud/wpn_4"
+ "pdaIcon" "guis/assets/hud/icons/grenade_new.tga"
+ "hudIcon" "guis/assets/hud/icons/grenadew.tga"
+
+ "weapon_class" "rvmWeaponHandgrenade"
+ "def_projectile" "projectile_grenade"
+ "def_explode_inhand" "env_grenade_explodeinhand"
+ "def_damage_inhand" "damage_grenadeSplash_inhand"
+ "ammoType" "ammo_grenades"
+ "ammoRequired" "1"
+ "clipSize" "0"
+ "silent_fire" "1"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "snd_acquire" "player_grenade_use"
+ "snd_respawn" "sound_weapon_respawn"
+ "snd_explode" "rocket_impact"
+
+ // skin_invisible not defined, weapon code will call UpdateSkin on weapon script
+ "skin_nade" "skins/models/weapons/nade"
+ "skin_nade_invis" "skins/models/weapons/nade_invis"
+ "skin_nonade" "skins/models/weapons/nonade"
+ "skin_nonade_invis" "skins/models/weapons/nonade_invis"
+
+ "spread" "0"
+
+ "primed_fuse" "3"
+
+ "controllerShakeHighMag" "0.3"
+ "controllerShakeHighTime" "50"
+ "controllerShakeLowMag" "0.6"
+ "controllerShakeLowTime" "100"
+}
+
+entityDef env_grenade_explodeinhand {
+ "spawnclass" "idAnimatedEntity"
+ "model" "grenadeExplosion.prt"
+}
+
+entityDef moveable_item_grenades {
+ "inherit" "weapon_handgrenade"
+ "spawnclass" "idMoveableItem"
+ "model" "models/weapons/grenades/grenades_world.lwo"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+ "snd_bounce" "smallbox"
+}
+
+model worldmodel_handgrenade {
+ mesh models/md5/weapons/grenade_world/worldgrenade.md5mesh
+ skin skins/models/weapons/nade.skin
+
+ anim idle models/md5/weapons/grenade_world/grenade_idle.md5anim
+ anim fire models/md5/weapons/grenade_world/grenade_throw.md5anim {
+ frame 44 object_call GrenadeNoNade
+ frame 69 object_call GrenadeNade
+ }
+}
+
+model viewmodel_grenade {
+ mesh models/md5/weapons/grenade_view/viewgrenade.md5mesh
+ offset ( 6 3 -3 )
+
+ anim raise models/md5/weapons/grenade_view/grenade_raise.md5anim {
+ frame 1 sound_weapon player_grenade_use
+ }
+ anim idle models/md5/weapons/grenade_view/grenade_idle.md5anim {
+ frame 1 object_call GrenadeNade
+ }
+ anim putaway models/md5/weapons/grenade_view/grenade_lower.md5anim
+ anim fire models/md5/weapons/grenade_view/grenade_fire2.md5anim
+ anim throw_start models/md5/weapons/grenade_view/grenade_start.md5anim {
+ // frame 1 sound_body player_handgrenade_throw
+ // Was frame 6 -Xian
+ }
+ anim throw models/md5/weapons/grenade_view/grenade_throw.md5anim {
+ frame 1 object_call GrenadeNade
+ frame 8 object_call GrenadeNoNade
+ }
+
+ anim throw_quick models/md5/weapons/grenade_view/grenade_fastthrow.md5anim {
+ frame 1 object_call GrenadeNade
+ frame 14 object_call GrenadeNoNade
+ }
+}
+
+entityDef damage_grenadeDirect {
+ "damage" "150"
+ "push" "1000"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_splat_flesh" ""
+ "gib" "1"
+ "smoke_wound_flesh" "bloodwound.prt"
+}
+
+entityDef damage_grenadeSplash {
+ "damage" "150"
+ "radius" "175"
+ "knockback" "80"
+ "push" "10000"
+ "attackerDamageScale" "0.2"
+ "attackerPushScale" "0.2"
+ "gib" "1"
+}
+
+entityDef damage_grenadeSplash_inhand {
+ "inherit" "damage_grenadeSplash"
+ "selfDamageScale" "20"
+}
+
+entityDef projectile_grenade {
+ "spawnclass" "idProjectile"
+ "mins" "-3 -3 0"
+ "maxs" "3 3 11"
+ "cylinder" "6"
+ "model" "models/weapons/grenades/grenades_world.lwo"
+ "offsetModel" "0 0 -5"
+
+ "def_damage" "damage_grenadeDirect"
+ "def_splash_damage" "damage_grenadeSplash"
+
+ "launchFromBarrel" "1"
+ "health" "0" // amount of damage projectile can take if damaged (0 means it can't be destroyed)
+ "velocity" "300 0 40" // how fast the projectile leaves the gun (or distance if fuse is 0)
+ "angular_velocity" "10 -10 10" // how the projectile is rotating when it leaves the gun
+ "thrust" "0" // the rate of acceleration (always in the direction of the projectiles model)
+ "thrust_start" "0" // when to start accelerating
+ "thrust_end" "0" // when to stop accelerating
+ "linear_friction" "0.6" // "air" friction
+ "angular_friction" "0.4"
+ "contact_friction" "0.1"
+ "bounce" "0.9" // how much speed a projectile retains when it bounces off of objects (coefficient of restitution). 0 means no bounce.
+ "mass" "10"
+ "gravity" "1066" // how much gravity affects the trajectory. gravity direction is same as the entity that fired it.
+ "fuse" "3" // how long before the projectile is removed or self-detonates. Use 0 for beam weapons (velocity == distance).
+
+ "detonate_on_fuse" "1" // whether projectile should detonate when it's fuse runs out
+ "detonate_on_death" "0" // whether projectile should detonate when it's "killed" (health runs out)
+ "detonate_on_world" "0" // whether projectile should detonate when it hits an obstacle
+ "detonate_on_actor" "1" // whether projectile should detonate when it hits a character in the game
+ "detonate_on_trigger" "1" // whether projectile should detonate when triggered by a character in the game
+
+ "impact_damage_effect" "1" // whether projectile causes blood splats from characters that bleed
+ "impact_gib" "1" // whether projectile can gib a character or not (for example: bullets don't gib, but rockets do)
+
+ "mtr_detonate" "textures/decals/ballburn01"
+ "decal_size" "300"
+
+ "model_detonate" "grenadeExplosion.prt" // parametric particles -- temp
+
+ "smoke_fly" "grenadetrail.prt" // particle effect while in the air
+ "smoke_detonate" "" // particle effect when detonates
+ "smoke_residue" "" // particle effect while in the air
+ "smoke_fuse" ""
+ "smoke_bounce" "bulletricochet.prt"
+
+ "debris_count" "6"
+ "def_debris" "debris_largeshrapnel"
+ "def_shrapnel" "debris_shrapnel"
+
+ "mtr_light_shader" ""
+ "light_color" ""
+ "light_radius" ""
+ "light_offset" ""
+
+ "mtr_explode_light_shader" "muzzleflash"
+ "explode_light_color" "1 0.8 0.4"
+ "explode_light_radius" "200"
+ "explode_light_fadetime" "0.25"
+
+ "detonation_axis" "0 0 1"
+
+ "snd_impact" "player_handgrenade_bounce"
+ "snd_bounce" "player_handgrenade_bounce"
+ "snd_explode" "rocket_impact"
+ "snd_throw" "player_handgrenade_throw" // For precache -- referenced in the script
+}
diff --git a/base/def/weapon_machinegun.def b/base/def/weapon_machinegun.def
new file mode 100644
index 00000000..7dc25003
--- /dev/null
+++ b/base/def/weapon_machinegun.def
@@ -0,0 +1,238 @@
+/***********************************************************************
+
+ weapon_machinegun.def
+
+***********************************************************************/
+
+export fred {
+ // view model machinegun
+ options -prefix MGUN_ -keep barrel eject guilight flash -parent barrel Ext -parent eject Ext -parent guilight Bod -parent flash Bod -rename origin blah -sourcedir models/weapons/machinegun/cycles -destdir models/md5/weapons/machinegun_view
+
+ mesh idle1.ma -dest viewmachinegun
+ anim idle1.ma -range 1 45 -dest idle
+ anim fire1.ma -range 1 4 -dest fire1
+ anim fire2.ma -range 1 4 -dest fire2
+ anim fire4.ma -range 1 4 -dest fire4
+ anim putaway.ma -dest putaway
+ anim pullup1.ma -dest pullup
+ anim reload1.ma -range 1 60 -dest reload
+ anim empty.ma -dest empty
+
+ // world model machinegun
+ options -ignorescale -prefix PLAYER_MG -keep ATTACHER barrel guilight eject flash -parent guilight Body -parent flash Body -parent eject Body -rename MGbarrel barrel -rename ATTACHER origin -clearorigin
+ addoptions -skipmesh PLAYER_mgdisplaymesh
+ addoptions -skipmesh PLAYER_mgextendermesh
+ addoptions -skipmesh PLAYER_mgbodymesh
+ addoptions -skipmesh PLAYER_mgclipmesh
+ addoptions -skipmesh PLAYER_mgtriggermesh
+
+ mesh models/characters/player/mocap/machinegun_idle.mb -dest models/md5/weapons/machinegun_world/worldmachinegun
+ anim models/characters/player/mocap/machinegun_idle.mb -dest models/md5/weapons/machinegun_world/machinegun_idle
+ anim models/characters/player/mocap/machinegun_reload.mb -dest models/md5/weapons/machinegun_world/machinegun_reload
+}
+
+entityDef weapon_machinegun {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Machinegun"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/machinegun/w_machinegun.lwo"
+ "model_view" "viewmodel_machinegun"
+ "model_world" "worldmodel_machinegun"
+ "joint_attach" "MGATTACHER"
+ "def_dropItem" "moveable_item_machinegun"
+
+ "weaponAngleOffsetAverages" "10"
+ "weaponAngleOffsetScale" ".25"
+ "weaponAngleOffsetMax" "15"
+ "weaponOffsetTime" "400"
+ "weaponOffsetScale" "0.005"
+
+
+ "inv_name" "Machinegun"
+ "inv_weapon" "weapon_machinegun"
+ "inv_ammo_clip" "10"
+ "inv_item" "5"
+
+ "modelindex" "MODELINDEX_MACHINEGUN"
+
+ "weapon_class" "rvmWeaponMachineGun"
+ "def_projectile" "projectile_bullet_machinegun"
+ "ammoType" "ammo_clip"
+ "ammoRequired" "1"
+ "clipSize" "60"
+ "lowAmmo" "10"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+
+ "gui" "guis/weapons/machinegun.gui"
+ "mtr_guiLightShader" "lights/viewWeaponGuiLight"
+
+ "icon" "guis/assets/hud/wpn_3"
+ "muzzle_kick_time" "0.2"
+ "muzzle_kick_maxtime" "0.6"
+ "muzzle_kick_angles" "0 0 0"
+ "muzzle_kick_offset" "1 1 2"
+ "recoilTime" "145"
+ "recoilAngles" "-1 0 0"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "smoke_muzzle" "mgmuzzlesmoke.prt"
+ "def_ejectBrass" "debris_brass"
+ "ejectBrassDelay" "10"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+ "snd_lowammo" "player_machinegun_lowammo"
+ "snd_glass" "bullet_impact_glass"
+ "skin_invisible" "skins/machinegun_invis"
+
+ "spread" "1"
+}
+
+entityDef moveable_item_machinegun {
+ "inherit" "weapon_machinegun"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_machinegun {
+ mesh models/md5/weapons/machinegun_world/worldmachinegun.md5mesh
+
+ anim raise models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+ anim idle models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+ anim aim models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+ anim fire1 models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+ anim reload models/md5/weapons/machinegun_world/machinegun_reload.md5anim
+ anim noammo models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+ anim putaway models/md5/weapons/machinegun_world/machinegun_idle.md5anim
+}
+
+model viewmodel_machinegun {
+ mesh models/md5/weapons/machinegun_view/viewmachinegun.md5mesh
+ anim raise models/md5/weapons/machinegun_view/pullup.md5anim {
+ frame 1 sound_body player_machinegun_raise
+ }
+ anim idle models/md5/weapons/machinegun_view/idle.md5anim
+ anim fire1 models/md5/weapons/machinegun_view/fire4.md5anim {
+ frame 1 sound_voice player_machinegun_fire
+ frame 1 sound_voice2 player_machinegun_mech
+ }
+ anim reload models/md5/weapons/machinegun_view/reload.md5anim {
+ frame 1 sound_item player_machinegun_reload
+ }
+ anim noammo models/md5/weapons/machinegun_view/empty.md5anim {
+ frame 1 sound_voice player_machinegun_empty
+ }
+ anim putaway models/md5/weapons/machinegun_view/putaway.md5anim
+}
+
+entityDef damage_bullet_machinegun {
+ "minDamage" "7"
+ "maxDamage" "9"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "2"
+
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_wound_metal" "textures/decals/hurtmetal"
+ "mtr_wound_ricochet" "textures/decals/hurtmetal"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_splat_flesh3" "textures/decals/dsplat7"
+ "mtr_splat_flesh4" "textures/decals/dsplat11"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+
+ "gib" "1"
+ "smoke_wound_flesh" "burstysquirt.prt"
+
+}
+
+entityDef projectile_bullet_machinegun {
+ "spawnclass" "idProjectile"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+ "cone" "3"
+// "model" "models/items/projectiles/rocket.ase"
+ "scale" "0.1"
+ "noshadows" "1"
+
+ "model_tracer" "models/particles/tracer/tracer.lwo"
+
+ "def_damage" "damage_bullet_machinegun"
+
+ "launchFromBarrel" "0"
+ "health" "0"
+ "velocity" "7200 0 0" // .38 Special 600 fps. Source: Petzal, David E. "How fast is a speeding bullet." Field and Stream. 97 (1992): 23
+ "angular_velocity" "0 0 0"
+ "thrust" "0"
+ "thrust_start" "0"
+ "thrust_end" "0"
+ "linear_friction" "0"
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6"
+ "mass" "1"
+ "gravity" "0"
+ "fuse" "4"
+
+ "detonate_on_fuse" "0"
+ "detonate_on_death" "0"
+ "detonate_on_world" "1"
+ "detonate_on_actor" "1"
+
+ "impact_damage_effect" "1"
+ "impact_gib" "1"
+
+ "mtr_detonate" "textures/decals/bulleth02"
+ "mtr_detonate_glass" "textures/decals/testbulletglass1"
+ "decal_size" "10"
+ "model_smokespark" "bulletsmokeandspark.prt"
+ "model_ricochet" "bulletricochet.prt"
+
+ "smoke_fly" ""
+ "smoke_detonate" "smokeandspark.smoke"
+ "smoke_bounce" "" //particles/bullet_sparks.particle"
+ "smoke_fuse" ""
+
+ "mtr_light_shader" ""
+ "light_color" "0 0 0"
+ "light_radius" "0"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" ""
+ "explode_light_color" "0 0 0"
+ "explode_light_radius" "0"
+ "explode_light_fadetime" "0"
+
+ "snd_tracer" "bullet_flight"
+ "snd_ricochet" "bullet_ricochet"
+ "snd_flesh" "bullet_impact_flesh"
+ "snd_metal" "bullet_impact_metal"
+ "snd_stone" "bullet_impact_stone"
+ "snd_wood" "bullet_impact_wood"
+ "snd_cardboard" "bullet_impact_cardboard"
+ "snd_glass" "bullet_impact_glass"
+ "snd_liquid" "bullet_impact_liquid"
+ "snd_plastic" "small_plastic"
+ //"snd_impact" "bullet_impact_metal"
+}
diff --git a/base/def/weapon_pda.def b/base/def/weapon_pda.def
new file mode 100644
index 00000000..1f56e11b
--- /dev/null
+++ b/base/def/weapon_pda.def
@@ -0,0 +1,75 @@
+/***********************************************************************
+
+ item_pda.def
+
+***********************************************************************/
+
+export fred {
+ options -prefix PDA_ -keep guilight -parent guilight pdabody -sourcedir models/items/pda/cycles -destdir models/md5/items/pda_view
+ mesh idle.mb
+ anim idle.mb
+ anim idle2.mb
+ anim idle3.mb
+ anim raise.mb
+ anim raise2.mb
+ anim lower.mb
+ anim lower2.mb
+}
+
+entityDef weapon_pda {
+ //"inherit" "item_default"
+ "model" "models/items/pda/pda_world.lwo"
+ "model_view" "viewmodel_pda"
+ "model_world" "worldmodel_pda"
+ "joint_attach" "PDA_ATTACHER"
+
+ "size" "32 32 32"
+
+ "inv_name" "PDA"
+ "inv_weapon" "weapon_pda"
+ "inv_item" "1"
+ "snd_acquire" "guisounds_compup"
+
+ "mtr_guiLightShader" "lights/viewWeaponGuiLight"
+
+ "hide_time" "0" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "0" // distance to offset weapon when talking to npcs or looking at guis
+
+ "weapon_class" "rvmWeaponPDA"
+
+ "spawnclass" "idItem"
+ "ammoRequired" "0"
+ "clipSize" "0"
+}
+
+model viewmodel_pda {
+ mesh models/md5/items/pda_view/idle.md5mesh
+ offset ( 12.5 -5 -12 )
+
+ anim cdraise models/md5/items/pda_view/raise.md5anim
+ anim raise models/md5/items/pda_view/raise2.md5anim {
+ frame 1 sound_body pda_raise
+ }
+ anim idle1 models/md5/items/pda_view/idle.md5anim
+ anim idle2 models/md5/items/pda_view/idle2.md5anim
+ anim putaway models/md5/items/pda_view/lower2.md5anim
+ anim fire1 models/md5/items/pda_view/raise.md5anim
+ anim fire2 models/md5/items/pda_view/idle3.md5anim
+}
+
+// world model pda
+
+export fred {
+
+ options -prefix PLAYER_PDA_ -keep ATTACHER -rename ATTACHER origin -clearorigin
+ addoptions -keepmesh PLAYER_polySurface1
+ addoptions -keepmesh PLAYER_polySurface2
+
+ mesh models/characters/male_npc/cycles/tweakedplayermoves/pda_idle.mb -dest models/md5/items/pda_world/worldpda
+ anim models/characters/male_npc/cycles/tweakedplayermoves/pda_idle.mb -dest models/md5/items/pda_world/pda_idle
+}
+
+model worldmodel_pda {
+ mesh models/md5/items/pda_world/worldpda.md5mesh
+ anim idle models/md5/items/pda_world/pda_idle.md5anim
+}
diff --git a/base/def/weapon_pistol.def b/base/def/weapon_pistol.def
new file mode 100644
index 00000000..f57a2484
--- /dev/null
+++ b/base/def/weapon_pistol.def
@@ -0,0 +1,249 @@
+/***********************************************************************
+
+ weapon_pistol.def
+
+ anim fire2 models/md5/weapons/pistol_view/fire2.md5anim {
+ frame 1 sound_weapon player_pistol_fire
+ }
+
+ anim fire3 models/md5/weapons/pistol_view/fire3.md5anim {
+ frame 1 sound_weapon player_pistol_fire
+ }
+
+ anim fire4 models/md5/weapons/pistol_view/fire4.md5anim {
+ frame 1 sound_weapon player_pistol_fire
+ }
+
+
+***********************************************************************/
+
+export fred {
+ // view model pistol
+ options -prefix PISTOL_ -keep barrel eject flash -parent flash Bod -parent barrel Bar -parent Ext eject -parent Bar Bod -parent eject Bod -parent Bod Rhand1 -parent Luparm1 Rloarm1 -parent Cli Bod -parent Trig Bod -rename origin blah -sourcedir models/weapons/pistol/fred/cycles -destdir models/md5/weapons/pistol_view
+
+ mesh idle.ma -dest viewpistol
+ anim idle.ma
+ anim idle_empty.ma
+ anim fire1.ma
+ anim fire2.ma
+ anim fire3.ma
+ anim fire4.ma
+ anim put_away.ma
+ //anim models/weapons/pistol/fred/cycles/raise3.ma -dest models/md5/weapons/pistol_view/raise3
+ anim raise.ma
+ anim reload_empty.ma
+ anim reload_empty.ma -range 1 14 -dest empty_reload_start
+
+ // world model pistol
+ options -ignorescale -prefix PLAYER_PISTOL_ -keep pistolclip ATTACHER barrel flash eject -parent eject pbody -parent flash pbody -parent pistolclip pbody -rename pbarrel barrel -rename ATTACHER origin -clearorigin
+
+ addoptions -skipmesh PLAYER_pistolBarrel
+ addoptions -skipmesh PLAYER_pistolExtender
+ addoptions -skipmesh PLAYER_pistolTrigger
+ addoptions -skipmesh PLAYER_pistolBody
+ addoptions -skipmesh PLAYER_pistolClip1
+
+ mesh models/characters/player/mocap/pistol_idle.mb -dest models/md5/weapons/pistol_world/worldpistol
+ anim models/characters/player/mocap/pistol_idle.mb -dest models/md5/weapons/pistol_world/pistol_idle
+}
+
+entityDef weapon_pistol {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Pistol"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/pistol/w_pistol.lwo"
+ "model_view" "viewmodel_pistol"
+ "model_world" "worldmodel_pistol"
+ "joint_attach" "PISTOL_ATTACHER"
+ "def_dropItem" "moveable_item_pistol"
+
+ "modelindex" "MODELINDEX_SHOTGUN"
+
+ "inv_name" "Pistol"
+ "inv_weapon" "weapon_pistol"
+ "inv_item" "5"
+ "inv_ammo_bullets" "12"
+ "icon" "guis/assets/hud/wpn_1"
+
+ "weapon_class" "rvmWeaponPistol"
+ "def_projectile" "projectile_bullet_pistol"
+ "ammoType" "ammo_bullets"
+ "ammoRequired" "1"
+ "clipSize" "12"
+ "lowAmmo" "4"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "recoilTime" "125"
+ "recoilAngles" "-1 0 0"
+
+ "weaponAngleOffsetAverages" "10"
+ "weaponAngleOffsetScale" ".25"
+ "weaponAngleOffsetMax" "10"
+ "weaponOffsetTime" "400"
+ "weaponOffsetScale" "0.005"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "smoke_muzzle" "muzzlesmoke.prt"
+ "def_ejectBrass" "debris_brass"
+ "ejectBrassDelay" "10"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+
+ "skin_invisible" "skins/pistol_invis"
+
+ "spread" "0"
+}
+
+entityDef moveable_item_pistol {
+ "inherit" "weapon_pistol"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_pistol {
+ mesh models/md5/weapons/pistol_world/worldpistol.md5mesh
+
+ anim idle models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim idle_empty models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim aim models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim fire1 models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim putaway models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim raise models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim reload models/md5/weapons/pistol_world/pistol_idle.md5anim
+ anim noammo models/md5/weapons/pistol_world/pistol_idle.md5anim
+}
+
+model viewmodel_pistol {
+ mesh models/md5/weapons/pistol_view/viewpistol.md5mesh
+
+ anim raise models/md5/weapons/pistol_view/raise.md5anim {
+ frame 7 sound_body player_pistol_raise_01
+ }
+ anim idle models/md5/weapons/pistol_view/idle.md5anim
+ anim idle_empty models/md5/weapons/pistol_view/idle_empty.md5anim
+
+ anim fire1 models/md5/weapons/pistol_view/fire1.md5anim {
+ frame 1 sound_weapon player_pistol_fire
+ }
+
+
+
+ anim reload models/md5/weapons/pistol_view/reload_empty.md5anim {
+ frame 1 sound_weapon player_pistol_reload
+ }
+
+ anim noammo models/md5/weapons/pistol_view/empty_reload_start.md5anim {
+ frame 1 sound_body player_pistol_empty
+ }
+
+ anim putaway models/md5/weapons/pistol_view/put_away.md5anim
+}
+
+entityDef damage_bullet_pistol {
+ "minDamage" "9"
+ "maxDamage" "14"
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "1"
+
+ "gib" "1"
+
+
+ "smoke_wound_flesh" "burstysquirt.prt"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_wound_metal" "textures/decals/hurtmetal"
+ "mtr_wound_ricochet" "textures/decals/hurtmetal"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_splat_flesh3" "textures/decals/dsplat7"
+ "mtr_splat_flesh4" "textures/decals/dsplat11"
+ // the flesh impact is used in the damage so players hear it on their body
+ // the other impacts are played on detonation
+ "snd_flesh" "bullet_impact_flesh"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+}
+
+entityDef projectile_bullet_pistol {
+ "spawnclass" "idProjectile"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+ "cone" "3"
+ "scale" "0.1"
+ "noshadows" "1"
+
+ "def_damage" "damage_bullet_pistol"
+
+ "launchFromBarrel" "0"
+ "health" "0"
+ "velocity" "7200 0 0" // .38 Special 600 fps. Source: Petzal, David E. "How fast is a speeding bullet." Field and Stream. 97 (1992): 23
+ "angular_velocity" "0 0 0"
+ "thrust" "0"
+ "thrust_start" "0"
+ "thrust_end" "0"
+ "linear_friction" "0"
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6"
+ "mass" "2"
+ "gravity" "0"
+ "fuse" "4"
+
+ "detonate_on_fuse" "0"
+ "detonate_on_death" "0"
+ "detonate_on_world" "1"
+ "detonate_on_actor" "1"
+
+ "impact_damage_effect" "1"
+ "impact_gib" "1"
+
+ "mtr_detonate" "textures/decals/bulleth02"
+ "mtr_detonate_glass" "textures/decals/testbulletglass1"
+ "decal_size" "10"
+
+ "model_smokespark" "bulletsmokeandspark.prt"
+ "model_ricochet" "bulletricochet.prt"
+
+
+ // played on collision with non-damagable entities
+ "snd_plastic" "bullet_impact_plastic"
+ "snd_cardboard" "bullet_impact_cardboard"
+ "snd_flesh" "bullet_impact_flesh"
+ "snd_metal" "bullet_impact_metal"
+ "snd_stone" "bullet_impact_stone"
+ "snd_wood" "bullet_impact_wood"
+ "snd_glass" "bullet_impact_glass"
+ "snd_liquid" "bullet_impact_liquid"
+ "snd_ricochet" "bullet_ricochet"
+ //"snd_impact" "bullet_impact_metal"
+
+ "mtr_light_shader" ""
+ "light_color" "0 0 0"
+ "light_radius" "0"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" ""
+ "explode_light_color" "0 0 0"
+ "explode_light_radius" "0"
+ "explode_light_fadetime" "0"
+}
diff --git a/base/def/weapon_plasmagun.def b/base/def/weapon_plasmagun.def
new file mode 100644
index 00000000..a2783592
--- /dev/null
+++ b/base/def/weapon_plasmagun.def
@@ -0,0 +1,360 @@
+/***********************************************************************
+
+ weapon_plasmagun.def
+
+***********************************************************************/
+
+export fred {
+ // view model plasmagun
+ options -prefix PLASMA_ -keep barrel eject flash guilight newguithing ventlight -parent ventlight pgbody2 -parent barrel pgbody2 -parent eject pgbody2 -parent gui pgscreen -parent newguithing pgupmon -parent guilight pgupmon -parent fx pgbody2 -parent nut pgbody2 -rename origin blah -sourcedir models/weapons/plasmagun/cycles -destdir models/md5/weapons/plasmagun_view
+ mesh idle.mb -dest viewplasmagun
+ anim raise.mb
+ anim raise2.mb -range 1 90 -dest raise2up
+ anim raise2.mb -range 90 90 -dest raise2idle
+ anim idle.mb
+ anim fire1.mb
+ anim fire2.mb
+ anim reload.mb
+ anim lower.mb
+
+ // world model plasmagun
+ options -ignorescale -prefix PLAYER_pg -keep ATTACHER barrel eject flash guilight -parent barrel body2 -parent eject body2 -parent flash body2 -parent eject body2 -parent guilight body2 -rename ATTACHER origin -clearorigin
+ addoptions -skipmesh PLAYER_plasma_innerscopebot_mesh1
+ addoptions -skipmesh PLAYER_plasma_outerscopebot_mesh1
+ addoptions -skipmesh PLAYER_plasma_innerscopetop_mesh1
+ addoptions -skipmesh PLAYER_plasma_outerscopetop_mesh1
+ addoptions -skipmesh PLAYER_plasma_innerflap1_mesh1
+ addoptions -skipmesh PLAYER_plasma_outerflap1_mesh1
+ addoptions -skipmesh PLAYER_plasma_bolt_mesh1
+ addoptions -skipmesh PLAYER_plasma_screen_mesh1
+ addoptions -skipmesh PLAYER_plasma_body_mesh1
+ addoptions -skipmesh PLAYER_plasma_handle_mesh1
+ addoptions -skipmesh PLAYER_plasma_outerflap2_mesh1
+ addoptions -skipmesh PLAYER_plasma_innerflap2_mesh4
+ addoptions -skipmesh PLAYER_plasma_uppermon_mesh4
+
+ mesh models/characters/player/mocap/plasmagun_idle.mb -dest models/md5/weapons/plasmagun_world/worldplasmagun
+ anim models/characters/player/mocap/plasmagun_idle.mb -dest models/md5/weapons/plasmagun_world/plasmagun_idle
+}
+
+entityDef weapon_plasmagun {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Plasma Gun"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/plasmagun/plasmagun_world.lwo"
+ "model_view" "viewmodel_plasmagun"
+ "model_world" "worldmodel_plasmagun"
+ "joint_attach" "pgATTACHER"
+ "def_dropItem" "moveable_item_plasmagun"
+
+ "modelindex" "MODELINDEX_PLASMAGUN"
+
+ "inv_name" "Plasma Gun"
+ "inv_weapon" "weapon_plasmagun"
+ "inv_ammo_cells" "50"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_5"
+
+ "gui" "guis/weapons/plasmagun.gui"
+ "mtr_guiLightShader" "lights/viewWeaponGuiLight"
+
+ "weapon_class" "rvmWeaponPlasmaGun"
+ "def_projectile" "projectile_plasmablast"
+ "muzzle_launch" "1"
+ "ammoType" "ammo_cells"
+ "ammoRequired" "1"
+ "clipSize" "50"
+ "lowAmmo" "10"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "0.18 0.70 0.82"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "smoke_muzzle" "plasmamuzzlesmoke.prt"
+ "gib" "1"
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".40"
+ "weaponAngleOffsetMax" "20"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "nozzleFx" "1"
+ "nozzleFxFade" "100"
+ "nozzleGlowColor" "0.18 0.70 0.82"
+ "nozzleGlowOffset" "-6 2 1"
+ "nozzleGlowRadius" "8"
+ "mtr_nozzleGlowShader" "lights/plasmaglow"
+
+ //"snd_acquire" "sound_weapon_acquire"
+ "snd_acquire" "player_plasma_raise"
+ "snd_respawn" "sound_weapon_respawn"
+ "snd_hum" "player_plasma_idle"
+ "skin_invisible" "skins/plasma_invis"
+
+ "spread" "0"
+}
+
+entityDef moveable_item_plasmagun {
+ "inherit" "weapon_plasmagun"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_plasmagun {
+ mesh models/md5/weapons/plasmagun_world/worldplasmagun.md5mesh
+ //offset ( 1 2 1 )
+
+ anim raise models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim
+ anim idle models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim
+ anim aim models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim
+ anim fire1 models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim {
+ frame 1 sound_weapon plasma_fire
+ }
+ anim reload models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim {
+ frame 1 sound_weapon plasma_reload
+ }
+ anim noammo models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim {
+ frame 1 sound_weapon plasma_dryfire
+ }
+ anim putaway models/md5/weapons/plasmagun_world/plasmagun_idle.md5anim
+}
+
+model viewmodel_plasmagun {
+ mesh models/md5/weapons/plasmagun_view/viewplasmagun.md5mesh
+ anim raise models/md5/weapons/plasmagun_view/raise.md5anim {
+ frame 1 sound_weapon player_plasma_raise
+ }
+ anim guiup models/md5/weapons/plasmagun_view/raise2up.md5anim
+ anim guiidle models/md5/weapons/plasmagun_view/raise2idle.md5anim
+ anim idle models/md5/weapons/plasmagun_view/idle.md5anim
+ anim fire1 models/md5/weapons/plasmagun_view/fire1.md5anim {
+ frame 1 sound_weapon plasma_fire
+ }
+ anim fire2 models/md5/weapons/plasmagun_view/fire2.md5anim {
+ frame 1 sound_weapon plasma_fire
+ }
+ anim putaway models/md5/weapons/plasmagun_view/lower.md5anim
+ anim reload models/md5/weapons/plasmagun_view/reload.md5anim {
+ frame 1 sound_weapon plasma_reload
+ }
+ anim noammo models/md5/weapons/plasmagun_view/fire1.md5anim {
+ frame 1 sound_weapon plasma_dryfire
+ }
+}
+
+entityDef damage_plasmablast {
+ "minDamage" "19"
+ "maxDamage" "19"
+ "smoke_wound_flesh" "bloodwound.prt"
+ "gib" "1"
+
+}
+
+entityDef projectile_plasmablast {
+ "spawnclass" "idProjectile"
+ "mins" "-6 -6 -4"
+ "maxs" "6 6 4"
+ "cone" "3"
+ "model" "models/particles/plasma_bolt/plasma_bolt.lwo"
+
+ "def_damage" "damage_plasmablast"
+
+ "launchFromBarrel" "1"
+ "health" "0" // amount of damage projectile can take if damaged (0 means it can't be destroyed)
+ "velocity" "700 0 0" // how fast the projectile leaves the gun (or distance if fuse is 0)
+ "random_shader_spin" "1" // sets shader parm 5 which if appropriate the projectile shader will use this as an alternating spin
+ "angular_velocity" "0 0 0" // how the projectile is rotating when it leaves the gun
+ "thrust" "0" // the rate of acceleration (always in the direction of the projectiles model)
+ "thrust_start" "0" // when to start accelerating
+ "thrust_end" "0" // when to stop accelerating
+ "linear_friction" "0" // "air" friction
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6" // how much speed a projectile retains when it bounces off of objects (coefficient of restitution). 0 means no bounce.
+ "mass" "50"
+ "push" "4000" // additional impulse when projectile collides
+ "gravity" "0" // how much gravity affects the trajectory. gravity direction is same as the entity that fired it.
+ "fuse" "10" // how long before the projectile is removed or self-detonates. Use 0 for beam weapons (velocity == distance).
+
+ "detonate_on_fuse" "0" // whether projectile should detonate when it's fuse runs out
+ "detonate_on_death" "1" // whether projectile should detonate when it's "killed" (health runs out)
+ "detonate_on_world" "1" // whether projectile should detonate when it hits an obstacle
+ "detonate_on_actor" "1" // whether projectile should detonate when it hits a character in the game
+
+ "impact_damage_effect" "1" // whether projectile causes blood splats from characters that bleed
+ "impact_gib" "1" // whether projectile can gib a character or not (for example: bullets don't gib, but rockets do)
+
+ "mtr_detonate" "textures/decals/ballburn_plasma"
+ "decal_size" "70"
+
+ "smoke_fly" "plasmatrail.prt" // particle effect while in the air
+ "smoke_detonate" "" // particle effect when detonates
+ "smoke_residue" "" // particle effect while in the air
+ "smoke_bounce" "" // particle effect when bounces
+ "smoke_fuse" "" // particle effect when removed from fuse expiring
+
+ "model_detonate" "plasmaimpact.prt"
+
+// "mtr_light_shader" ""
+// "light_color" "0 0 0.8"
+// "light_radius" "160"
+// "light_offset" "-8 0 0"
+
+// "mtr_explode_light_shader" "muzzleflash"
+// "explode_light_color" "0 0 1"
+// "explode_light_radius" "160"
+// "explode_light_fadetime" "0.25"
+
+ "snd_fly" "plasma_flight" // sound effect while in the air
+ "snd_explode" "plasma_impact"
+}
+
+/*
+ Generated by the Particle Editor.
+ To use the particle editor, launch the game and type 'editParticles' on the console.
+*/
+particle plasmaimpact {
+ depthHack 10
+ {
+ count 1
+ material textures/particles/boomboom2
+ time 1.400
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 10.000 10.000 0.000
+ direction cone 0.000
+ orientation view
+ speed "0.000"
+ size "10.000" to "43.000"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.100
+ fadeOut 0.750
+ fadeIndex 0.000
+ color 0.000 0.360 0.490 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+ {
+ count 1
+ material textures/particles/boomboom
+ time 0.150
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 2.000 2.000 2.000
+ direction cone 0.000
+ orientation view
+ speed "0.200" to "0.000"
+ size "0.000" to "35.000"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.050
+ fadeOut 0.450
+ fadeIndex 0.000
+ color 0.200 0.880 1.000 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+ {
+ count 1
+ material textures/particles/boomboom
+ time 0.150
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 2.000 2.000 2.000
+ direction cone 0.000
+ orientation view
+ speed "0.000"
+ size "5.000" to "40.000"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.050
+ fadeOut 0.700
+ fadeIndex 0.000
+ color 0.010 0.870 1.000 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+ {
+ count 1
+ material textures/particles/boomboom2
+ time 0.150
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 7.000 7.000 7.000
+ direction cone 0.000
+ orientation view
+ speed "0.000"
+ size "0.000" to "60.500"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.050
+ fadeOut 0.350
+ fadeIndex 0.000
+ color 0.010 0.870 1.000 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+ {
+ count 1
+ material textures/particles/bfgballblast
+ time 0.250
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 7.000 7.000 7.000
+ direction cone 0.000
+ orientation z
+ speed "0.000"
+ size "0.000" to "30.000"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.050
+ fadeOut 0.350
+ fadeIndex 0.000
+ color 0.000 0.400 1.000 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+ {
+ count 1
+ material textures/particles/plasmablast
+ time 0.250
+ cycles 1.000
+ bunching 0.050
+ distribution sphere 7.000 7.000 7.000
+ direction cone 0.000
+ orientation view
+ speed "0.000"
+ size "5.000" to "35.000"
+ aspect "1.000"
+ randomDistribution 1
+ boundsExpansion 0.000
+ fadeIn 0.050
+ fadeOut 0.350
+ fadeIndex 0.000
+ color 0.000 0.400 1.000 1.000
+ fadeColor 0.000 0.000 0.000 0.000
+ offset 0.000 0.000 0.000
+ gravity world 0.000
+ }
+}
\ No newline at end of file
diff --git a/base/def/weapon_rocketlauncher.def b/base/def/weapon_rocketlauncher.def
new file mode 100644
index 00000000..8ce77a81
--- /dev/null
+++ b/base/def/weapon_rocketlauncher.def
@@ -0,0 +1,214 @@
+/***********************************************************************
+
+ weapon_rocketlauncher.def
+
+***********************************************************************/
+
+export fred {
+ // View Model Rocket Launcher
+ options -prefix RL_ -keep barrel -keep flash -rename origin blah -rename barrel2 barrel -parent barrel RLBODY -parent flash RLBODY
+
+ mesh models/weapons/rocketlauncher/cycles/idle.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/viewrocketlauncher
+ anim models/weapons/rocketlauncher/cycles/raise.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/raise
+ anim models/weapons/rocketlauncher/cycles/idle.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/idle
+ anim models/weapons/rocketlauncher/cycles/fire.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/fire
+ anim models/weapons/rocketlauncher/cycles/reload.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/reload
+ anim models/weapons/rocketlauncher/cycles/lower.mb -rotate 90 -dest models/md5/weapons/rocketlauncher_view/lower
+
+ // world model Rocket Launcher
+ options -prefix PLAYER_RL -keep barrel _ATTACHER flash eject -parent eject BODY -parent flash BODY -rename barrel2 barrel -rename _ATTACHER origin -parent barrel BODY -clearorigin
+ addoptions -skipmesh PLAYER_rshell1
+ addoptions -skipmesh PLAYER_rshell2
+ addoptions -skipmesh PLAYER_rshell3
+ addoptions -skipmesh PLAYER_rshell4
+ addoptions -skipmesh PLAYER_reloader1
+ addoptions -skipmesh PLAYER_handle
+ addoptions -skipmesh PLAYER_aimer
+ addoptions -skipmesh PLAYER_body
+
+ mesh models/characters/player/mocap/rocketlauncher_aim.mb -dest models/md5/weapons/rocketlauncher_world/worldrocketlauncher
+ //anim models/characters/player/mocap/rocketlauncher_aim.mb -dest models/md5/weapons/rocketlauncher_world/rocketlauncher_aim
+ anim models/characters/male_npc/cycles/tweakedplayermoves/rocketlauncher_aim.mb -dest models/md5/weapons/rocketlauncher_world/rocketlauncher_aim
+
+ //anim models/characters/player/mocap/rocketlauncher_fire.mb -dest models/md5/weapons/rocketlauncher_world/rocketlauncher_fire
+}
+
+entityDef weapon_rocketlauncher {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Rocket Launcher"
+ "editor_rotatable" "1"
+
+ "modelindex" "MODELINDEX_ROCKETLAUNCHER"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/rocketlauncher/w_rocketlauncher.lwo"
+ "model_view" "viewmodel_rocketlauncher"
+ "model_world" "worldmodel_rocketlauncher"
+ "joint_attach" "RL_ATTACHER"
+ "def_dropItem" "moveable_item_rocketlauncher"
+
+ "inv_name" "Rocket Launcher"
+ "inv_weapon" "weapon_rocketlauncher"
+ "inv_ammo_rockets" "5"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_6"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-20" // distance to offset weapon when talking to npcs or looking at guis
+
+ "weapon_class" "rvmWeaponRocketLauncher"
+ "def_projectile" "projectile_rocket"
+ "ammoType" "ammo_rockets"
+ "ammoRequired" "1"
+ "clipSize" "5"
+ "lowAmmo" "1"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "recoilTime" "325"
+ "recoilAngles" "-1 0 0"
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".5"
+ "weaponAngleOffsetMax" "50"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ //"smoke_muzzle" "rocketmuzzle.prt"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+
+ "skin_1" "models/weapons/0rox.skin"
+ "skin_2" "models/weapons/1rox.skin"
+ "skin_3" "models/weapons/2rox.skin"
+ "skin_4" "models/weapons/3rox.skin"
+ "skin_5" "models/weapons/4rox.skin"
+ "skin_6" "models/weapons/5rox.skin"
+
+ "skin_invisible" "skins/rocket_invis"
+
+ "spread" "1"
+}
+
+entityDef moveable_item_rocketlauncher {
+ "inherit" "weapon_rocketlauncher"
+ "spawnclass" "idMoveableItem"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_rocketlauncher {
+ mesh models/md5/weapons/rocketlauncher_world/worldrocketlauncher.md5mesh
+
+ anim raise models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim idle models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim aim models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim fire models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim reload models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim noammo models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+ anim putaway models/md5/weapons/rocketlauncher_world/rocketlauncher_aim.md5anim
+}
+
+model viewmodel_rocketlauncher {
+ mesh models/md5/weapons/rocketlauncher_view/viewrocketlauncher.md5mesh
+ offset ( -6 -3 -60 )
+
+ anim raise models/md5/weapons/rocketlauncher_view/raise.md5anim {
+ frame 1 sound_weapon player_rocketlauncher_raise
+ }
+ anim idle models/md5/weapons/rocketlauncher_view/idle.md5anim
+ anim fire models/md5/weapons/rocketlauncher_view/fire.md5anim {
+ frame 1 sound_weapon player_rocketlauncher_fire
+ }
+ anim reload models/md5/weapons/rocketlauncher_view/reload.md5anim {
+ frame 1 sound_weapon player_rocketlauncher_reload
+ }
+ anim putaway models/md5/weapons/rocketlauncher_view/lower.md5anim
+}
+
+entityDef projectile_rocket {
+ "spawnclass" "idProjectile"
+ "mins" "-3 -3 -6"
+ "maxs" "3 3 6"
+ "cone" "3"
+ "model" "models/weapons/rocketlauncher/rocket.lwo"
+
+ "def_damage" "damage_rocketDirect"
+ "def_splash_damage" "damage_rocketSplash"
+
+ "launchFromBarrel" "1" // launch from barrel
+ "health" "5" // amount of damage projectile can take if damaged (0 means it can't be destroyed)
+ "velocity" "900 0 0" // how fast the projectile leaves the gun (or distance if fuse is 0)
+ "angular_velocity" "0 0 0" // how the projectile is rotating when it leaves the gun
+ "thrust" "0" // the rate of acceleration (always in the direction of the projectiles model)
+ "thrust_start" "0" // when to start accelerating
+ "thrust_end" "4" // when to stop accelerating
+ "linear_friction" "0" // "air" friction
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0" // how much speed a projectile retains when it bounces off of objects (coefficient of restitution). 0 means no bounce.
+ "mass" "300"
+ "gravity" "0" // how much gravity affects the trajectory. gravity direction is same as the entity that fired it.
+ "fuse" "10" // how long before the projectile is removed or self-detonates. Use 0 for beam weapons (velocity == distance).
+
+ "detonate_on_fuse" "0" // whether projectile should detonate when it's fuse runs out
+ "detonate_on_death" "1" // whether projectile should detonate when it's "killed" (health runs out)
+ "detonate_on_world" "1" // whether projectile should detonate when it hits an obstacle
+ "detonate_on_actor" "1" // whether projectile should detonate when it hits a character in the game
+
+ "impact_damage_effect" "1" // whether projectile causes blood splats from characters that bleed
+ "impact_gib" "1" // whether projectile can gib a character or not (for example: bullets don't gib, but rockets do)
+
+ "mtr_detonate" "textures/decals/ballburn01"
+ "decal_size" "300"
+
+ "smoke_fly" "rockettrail.prt" // particle effect while in the air
+ // parametric particles -- temp
+ "model_detonate" "rocketExplosion.prt"
+ "smoke_detonate" "" // particle effect when detonates
+ "smoke_residue" "" // particle effect while in the air
+ "smoke_bounce" ""
+ "smoke_fuse" ""
+
+ "debris_count" "6"
+ "def_debris" "debris_largeshrapnel"
+ "def_shrapnel" "debris_shrapnel"
+
+ "mtr_light_shader" ""
+ "light_color" "1 0.8 0.4"
+ "light_radius" "160"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" "muzzleflash"
+ "explode_light_color" "2 1.6 0.8"
+ "explode_light_radius" "320"
+ "explode_light_fadetime" "0.5"
+
+ "snd_fly" "rocket_flight" // sound effect while in the air
+ "snd_explode" "rocket_impact"
+}
+
+entityDef damage_rocketDirect {
+ "damage" "170"
+ "push" "1000"
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_splat_flesh" ""
+ "gib" "1"
+ "smoke_wound_flesh" "bloodwound.prt"
+}
+
+entityDef damage_rocketSplash {
+ "damage" "150"
+ "radius" "175" //changed by Tim 6-22-04 from 132
+ "knockback" "80"
+ "push" "10000"
+ "attackerDamageScale" "0.2"
+ "attackerPushScale" "0.2"
+ "gib" "1"
+}
diff --git a/base/def/weapon_shotgun.def b/base/def/weapon_shotgun.def
new file mode 100644
index 00000000..6bef5e63
--- /dev/null
+++ b/base/def/weapon_shotgun.def
@@ -0,0 +1,287 @@
+/***********************************************************************
+
+ weapon_shotgun.def
+
+***********************************************************************/
+
+export fred {
+ // view model shotgun
+ options -prefix SG_ -keep barrel eject shell flash -parent flash body -rename barrel2 barrel -parent barrel body -parent eject body -parent body Rhand -parent shell Lhand -rename origin blah -sourcedir models/weapons/shotgun/cycles -destdir models/md5/weapons/shotgun_view
+ mesh idle.mb -dest viewshotgun
+ anim idle.mb
+ anim raise.mb
+ anim fire1.mb
+ anim fire2.mb
+ anim fire3.mb
+ anim reload_start.mb
+ anim reload_loop.mb
+ anim reload_loop2.mb
+ anim reload_loop3.mb
+ anim reload_end.mb
+ anim reload2.mb
+ anim lower.mb
+ anim empty.mb
+
+ // world model shotgun
+ options -prefix PLAYER_SHOTGUN_ -keep ATTACHER barrel flash eject -parent flash sgbody -parent eject sgbody -rename ATTACHER origin -rename sgbarrel barrel -clearorigin
+ addoptions -skipmesh PLAYER_shotgun_body_mesh
+ addoptions -skipmesh PLAYER_shotgun_handle_mesh
+ addoptions -skipmesh PLAYER_shotgun_trigger_mesh
+ addoptions -skipmesh PLAYER_shotgun_loflap_mesh
+ addoptions -skipmesh PLAYER_shotgun_extender_mesh
+ addoptions -skipmesh PLAYER_shotgun_upflap1_mesh
+ addoptions -skipmesh PLAYER_shotgun_upflap2_mesh
+
+ mesh models/characters/player/mocap/shotgun_idle.mb -dest models/md5/weapons/shotgun_world/worldshotgun
+ anim models/characters/player/mocap/shotgun_idle.mb -dest models/md5/weapons/shotgun_world/shotgun_idle
+ anim models/characters/player/mocap/shotgun_fire.mb -range 49 75 -dest models/md5/weapons/shotgun_world/shotgun_fire1
+}
+
+entityDef weapon_shotgun {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Shotgun"
+ "editor_rotatable" "1"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/shotgun/w_shotgun2.lwo"
+ "model_view" "viewmodel_shotgun"
+ "model_world" "worldmodel_shotgun"
+ "joint_attach" "SHOTGUN_ATTACHER"
+ "def_dropItem" "moveable_item_shotgun"
+
+ "modelindex" "MODELINDEX_SHOTGUN"
+
+ "inv_name" "Shotgun"
+ "inv_weapon" "weapon_shotgun"
+ "inv_ammo_shells" "4"
+ "inv_item" "5"
+ "icon" "guis/assets/hud/wpn_2"
+
+ "weapon_class" "rvmWeaponShotgun"
+ "def_projectile" "projectile_bullet_shotgun"
+ "ammoType" "ammo_shells"
+ "ammoRequired" "1"
+ "clipSize" "8"
+ "lowAmmo" "2"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "recoilTime" "325"
+ "recoilAngles" "-1 0 0"
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".40"
+ "weaponAngleOffsetMax" "20"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "smoke_muzzle" "shotgunmuzzlesmoke.prt"
+ "def_ejectBrass" "debris_shotgunbrass"
+ "ejectBrassDelay" "650"
+
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+ "skin_invisible" "skins/shotgun_invis"
+
+ "spread" "22"
+}
+
+entityDef moveable_item_shotgun {
+ "inherit" "weapon_shotgun"
+ "spawnclass" "idMoveableItem"
+ "model" "models/weapons/shotgun/w_shotgun2.lwo"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+}
+
+model worldmodel_shotgun {
+ mesh models/md5/weapons/shotgun_world/worldshotgun.md5mesh
+
+ anim raise models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim idle models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim aim models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim fire1 models/md5/weapons/shotgun_world/shotgun_fire1.md5anim
+ anim fire2 models/md5/weapons/shotgun_world/shotgun_fire1.md5anim
+ anim fire3 models/md5/weapons/shotgun_world/shotgun_fire1.md5anim
+ anim reload_start models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim reload_loop1 models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim reload_loop2 models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim reload_end models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+ anim putaway models/md5/weapons/shotgun_world/shotgun_idle.md5anim
+}
+
+model viewmodel_shotgun {
+ mesh models/md5/weapons/shotgun_view/viewshotgun.md5mesh
+
+ anim raise models/md5/weapons/shotgun_view/raise.md5anim {
+ frame 1 sound_body player_shotgun_raise
+ }
+ anim idle models/md5/weapons/shotgun_view/idle.md5anim
+ anim fire1 models/md5/weapons/shotgun_view/fire1.md5anim {
+ frame 1 sound_weapon player_shotgun_fire
+ frame 17 sound_voice player_shotgun_pump
+ }
+ anim fire2 models/md5/weapons/shotgun_view/fire2.md5anim {
+ frame 1 sound_weapon player_shotgun_fire
+ frame 17 sound_voice player_shotgun_pump
+ }
+ anim fire3 models/md5/weapons/shotgun_view/fire3.md5anim {
+ frame 1 sound_weapon player_shotgun_fire
+ frame 17 sound_voice player_shotgun_pump
+ }
+ anim reload_start models/md5/weapons/shotgun_view/reload_start.md5anim {
+ frame 1 sound player_shotgun_reload_start
+ }
+ anim reload_loop1 models/md5/weapons/shotgun_view/reload_loop.md5anim {
+ frame 4 sound player_shotgun_reload
+ }
+ anim reload_loop2 models/md5/weapons/shotgun_view/reload_loop2.md5anim {
+ frame 4 sound player_shotgun_reload
+ }
+ anim reload_loop3 models/md5/weapons/shotgun_view/reload_loop3.md5anim {
+ frame 4 sound player_shotgun_reload
+ }
+ anim reload_end models/md5/weapons/shotgun_view/reload_end.md5anim {
+ //frame 1 sound player_shotgun_reload_end
+ frame 14 sound_voice player_shotgun_pump
+ }
+ anim noammo models/md5/weapons/shotgun_view/empty.md5anim {
+ frame 1 sound_voice player_shotgun_empty
+ }
+ anim putaway models/md5/weapons/shotgun_view/lower.md5anim
+}
+
+entityDef damage_shotgun {
+ "minDamage" "4"
+ "maxDamage" "7"
+
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "10"
+
+ "gib" "1"
+ "smoke_wound_flesh" "burstysquirt.prt"
+
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_wound_metal" "textures/decals/hurtmetal"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_splat_flesh3" "textures/decals/dsplat7"
+ "mtr_splat_flesh4" "textures/decals/dsplat11"
+ "snd_flesh" "shotgun_impact_flesh"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+}
+
+entityDef debris_shotgunbrass {
+ "spawnclass" "idDebris"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+
+ "model" "models/weapons/shell1/sshell_bigger.lwo"
+
+ "health" "0" // amount of damage projectile can take if damaged (0 means it can't be destroyed)
+ "velocity" "60 30 10" // how fast the projectile leaves the gun (or distance if fuse is 0)
+ "angular_velocity" "0 0 0" // how the projectile is rotating when it leaves the gun
+ "thrust" "0" // the rate of acceleration (always in the direction of the projectiles model)
+ "thrust_start" "0" // when to start accelerating
+ "thrust_end" "0" // when to stop accelerating
+ "linear_friction" "0.6" // "air" friction
+ "angular_friction" "0.4"
+ "contact_friction" "0.1"
+ "bounce" "0.5" // how much speed a projectile retains when it bounces off of objects (coefficient of restitution). 0 means no bounce.
+ "mass" "1"
+ "gravity" "666" // how much gravity affects the trajectory. gravity direction is same as the entity that fired it.
+ "fuse" "3" // how long before the projectile is removed or self-detonates. Use 0 for beam weapons (velocity == distance).
+
+ "detonate_on_fuse" "1" // whether projectile should detonate when it's fuse runs out
+ "detonate_on_death" "0" // whether projectile should detonate when it's "killed" (health runs out)
+ "detonate_on_world" "0" // whether projectile should detonate when it hits an obstacle
+ "detonate_on_actor" "0" // whether projectile should detonate when it hits a character in the game
+ "snd_bounce" "casings_shotgun"
+}
+
+entityDef projectile_bullet_shotgun {
+ "spawnclass" "idProjectile"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+ "cone" "3"
+ //"model" "models/items/projectiles/rocket.ase"
+ "scale" "0.1"
+ "noshadows" "1"
+
+ "snd_flesh" "bullet_impact_flesh"
+ "snd_metal" "bullet_impact_metal"
+ "snd_stone" "bullet_impact_stone"
+ "snd_wood" "bullet_impact_wood"
+ "snd_cardboard" "bullet_impact_cardboard"
+ "snd_glass" "bullet_impact_glass"
+ "snd_liquid" "bullet_impact_liquid"
+ "snd_ricochet" "bullet_ricochet"
+ "snd_plastic" "small_plastic"
+
+ "snd_impact" "shotgun_impact_metal"
+
+ "def_damage" "damage_shotgun"
+
+ "launchFromBarrel" "0"
+ "health" "0"
+ "velocity" "7200 0 0" // .38 Special 600 fps. Source: Petzal, David E. "How fast is a speeding bullet." Field and Stream. 97 (1992): 23
+ "angular_velocity" "0 0 0"
+ "thrust" "0"
+ "thrust_start" "0"
+ "thrust_end" "0"
+ "linear_friction" "0"
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6"
+ "mass" "1.0"
+ "gravity" "0"
+ "fuse" "4"
+
+ "detonate_on_fuse" "0"
+ "detonate_on_death" "0"
+ "detonate_on_world" "1"
+ "detonate_on_actor" "1"
+
+ "impact_damage_effect" "1"
+ "impact_gib" "1"
+
+ "mtr_detonate" "textures/decals/bulleth02"
+ "mtr_detonate_glass" "textures/decals/testbulletglass1"
+ "decal_size" "10"
+ "model_smokespark" "sgsmokeandspark.prt"
+ "model_ricochet" "bulletricochet.prt"
+
+
+ "smoke_fly" ""
+ "smoke_detonate" "smokeandspark.smoke"
+ "smoke_bounce" "sparkdebris.smoke"
+ "smoke_fuse" ""
+
+ "mtr_light_shader" ""
+ "light_color" "0 0 0"
+ "light_radius" "0"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" ""
+ "explode_light_color" "0 0 0"
+ "explode_light_radius" "0"
+ "explode_light_fadetime" "0"
+}
diff --git a/base/def/weapon_shotgun_double.def b/base/def/weapon_shotgun_double.def
new file mode 100644
index 00000000..bb403141
--- /dev/null
+++ b/base/def/weapon_shotgun_double.def
@@ -0,0 +1,282 @@
+/***********************************************************************
+
+ Double Barrel Shotgun
+
+***********************************************************************/
+
+export dbshotgun {
+ // view model double barrel shotgun
+ options -prefix DB_ -keep barrel flash eject -parent flash gunbarrel -parent eject gunbody -parent barrel gunbarrel -sourcedir models/weapons/doublebarrel/cycles -destdir models/md5/weapons/doublebarrel_view/new
+
+
+ mesh idle.mb -dest dbviewmesh
+ anim idle.mb
+ anim raise_lower.mb -range 1 15 -dest raise
+ anim raise_lower.mb -range 15 21 -dest lower
+ anim fire1.mb -xyzprecision .01 -quatprecision 0.00001
+ anim fire2.mb -xyzprecision .01 -quatprecision 0.00001
+ anim reload.mb -xyzprecision .01 -quatprecision 0.00001
+ //anim reload2.mb
+ anim reload3.mb
+ anim reload4.mb
+ anim putaway.mb
+ anim reload_noammo.mb
+ anim noammo.mb
+}
+
+export jayshotgun {
+
+ // world model double shotgun
+ options -prefix PLAYER_DBSHOTGUN_ -keep ATTACHER barrel flash eject -parent flash body -parent eject body -rename ATTACHER origin -clearorigin
+
+ mesh models/characters/player_character/cycles/grabber_w_idle.mb -dest models/md5/weapons/shotgun_double_world/worlddbshotgun
+ anim models/characters/player_character/cycles/grabber_w_idle.mb -range 0 0 -dest models/md5/weapons/shotgun_double_world/dbshotgun_idle
+ anim models/characters/player_character/cycles/grabber_w_idle.mb -range 0 0 -dest models/md5/weapons/shotgun_double_world/dbshotgun_fire1
+ anim models/characters/player_character/cycles/dbshotgun_w_reload.mb -dest models/md5/weapons/shotgun_double_world/dbshotgun_reload
+}
+
+model worldmodel_shotgun_double {
+ mesh models/md5/weapons/shotgun_double_world/worlddbshotgun.md5mesh
+
+ anim raise models/md5/weapons/shotgun_double_world/dbshotgun_idle.md5anim
+ anim idle models/md5/weapons/shotgun_double_world/dbshotgun_idle.md5anim
+ anim aim models/md5/weapons/shotgun_double_world/dbshotgun_idle.md5anim
+ anim fire1 models/md5/weapons/shotgun_double_world/dbshotgun_fire1.md5anim
+ anim fire2 models/md5/weapons/shotgun_double_world/dbshotgun_fire1.md5anim
+ anim fire3 models/md5/weapons/shotgun_double_world/dbshotgun_fire1.md5anim
+ anim reload_start models/md5/weapons/shotgun_double_world/dbshotgun_reload.md5anim
+ anim putaway models/md5/weapons/shotgun_double_world/dbshotgun_idle.md5anim
+}
+
+
+
+
+model viewmodel_shotgun_double {
+ mesh models/md5/weapons/doublebarrel_view/new/dbviewmesh.md5mesh
+ offset ( 8.5 -4 -5 )
+
+ anim idle models/md5/weapons/doublebarrel_view/new/idle.md5anim
+
+ anim raise models/md5/weapons/doublebarrel_view/new/raise.md5anim {
+ frame 1 sound_body player_shotgun_raise
+ }
+ anim putaway models/md5/weapons/doublebarrel_view/new/lower.md5anim {
+ frame 1 sound_body player_shotgun_putaway
+ }
+
+ anim fire1 models/md5/weapons/doublebarrel_view/new/fire1.md5anim {
+ frame 1 sound_weapon snd_fire
+ }
+ anim fire2 models/md5/weapons/doublebarrel_view/new/fire2.md5anim {
+ frame 1 sound_weapon snd_fire
+ }
+
+ anim reload_start models/md5/weapons/doublebarrel_view/new/reload.md5anim {
+ frame 6 sound_body3 snd_click
+ frame 23 sound_voice snd_insert
+ frame 32 sound_voice2 snd_insert
+ frame 45 sound_body2 snd_clack
+ frame 26 object_call EjectBrass
+ }
+ anim reload_newammo models/md5/weapons/doublebarrel_view/new/reload.md5anim {
+ frame 6 sound_body3 snd_click
+ frame 23 sound_voice snd_insert
+ frame 32 sound_voice2 snd_insert
+ frame 45 sound_body2 snd_clack
+ }
+ anim reload_noammo models/md5/weapons/doublebarrel_view/new/idle.md5anim {
+ frame 1 sound player_shotgun_reload_start
+ }
+
+ anim noammo models/md5/weapons/doublebarrel_view/new/noammo.md5anim {
+ frame 1 sound_voice player_shotgun_empty
+ }
+}
+
+
+
+
+entityDef weapon_shotgun_double {
+ "editor_color" ".3 .3 1"
+ "editor_mins" "-16 -16 0"
+ "editor_maxs" "16 16 32"
+ "editor_usage" "Double Barrel Shotgun"
+ "editor_rotatable" "1"
+
+ "modelindex" "MODELINDEX_SHOTGUN"
+
+ "spawnclass" "idItem"
+ "size" "32 32 32"
+ "model" "models/weapons/doublebarrel/doublebarrel_w.lwo"
+ "mp_model" "models/weapons/doublebarrel/doublebarrel_w.lwo"
+ "model_view" "viewmodel_shotgun_double"
+ "model_world" "worldmodel_shotgun_double"
+ "joint_attach" "SHOTGUN_ATTACHER"
+ "def_dropItem" "moveable_item_shotgun_double"
+
+ "inv_name" "#str_00100191"
+ "inv_weapon" "weapon_shotgun_double"
+ "inv_ammo_shells" "8"
+ "inv_item" "5"
+ "inv_icon" "guis/assets/hud/items/shotgun_ammo.tga"
+ "icon" "guis/assets/hud/wpn_2"
+
+ "inv_desc" "#str_00100212"
+ "display_name" "#str_00100191"
+ "hudIcon" "guis/assets/hud/icons/doublebarrelw.tga"
+
+ "weapon_class" "rvmWeaponDoubleShotgun"
+ "def_projectile" "projectile_bullet_shotgun_double"
+ "ammoType" "ammo_shells"
+ "ammoRequired" "1"
+ "clipSize" "2"
+ "minclipsize" "2"
+ "lowAmmo" "0"
+ "mtr_flashShader" "muzzleflash"
+ "flashColor" "1 0.8 0.4"
+ "flashRadius" "120"
+ "silent_fire" "0"
+ "recoilTime" "325"
+ "recoilAngles" "-1 0 0"
+
+ "weaponAngleOffsetAverages" "15"
+ "weaponAngleOffsetScale" ".40"
+ "weaponAngleOffsetMax" "20"
+ "weaponOffsetTime" "500"
+ "weaponOffsetScale" "0.005"
+
+ "hide_time" "0.3" // time to hide weapon when talking to npcs or looking at guis
+ "hide_distance" "-15" // distance to offset weapon when talking to npcs or looking at guis
+
+ "smoke_muzzle" "shotgunmuzzlesmoke.prt"
+ "def_ejectBrass" "debris_shotgunbrass"
+ "ejectBrassDelay" "-1"
+
+
+ "snd_fire" "ssg_fire"
+ "snd_click" "ssg_click"
+ "snd_clack" "ssg_clack"
+ "snd_insert" "ssg_shell_insert"
+ "snd_acquire" "sound_weapon_acquire"
+ "snd_respawn" "sound_weapon_respawn"
+
+ "skin_invisible" "skins/shotgun_double_invis"
+
+ "controllerShakeHighMag" "2.0"
+ "controllerShakeHighTime" "220"
+ "controllerShakeLowMag" "20.9"
+ "controllerShakeLowTime" "350"
+}
+
+entityDef moveable_item_shotgun_double {
+ "inherit" "weapon_shotgun_double"
+ "spawnclass" "idMoveableItem"
+ "model" "models/weapons/doublebarrel/doublebarrel_w.lwo"
+ "density" "0.05"
+ "friction" "0.2"
+ "bouncyness" "0"
+ "snd_bounce" "smallbox"
+ "def_damage" "damage_moveable_player"
+ "monster_damage" "damage_moveable_tiny"
+ "damageWhenActive" "1"
+}
+
+entityDef damage_shotgun_double {
+ "minDamage" "12"
+ "maxDamage" "20"
+
+ "kickDir" "1 0 0"
+
+ "mtr_blob" "genericDamage"
+ "blob_time" "300"
+ "blob_size" "400"
+ "blob_offset_x" "400"
+
+ "knockback" "12"
+
+ "gib" "1"
+ "smoke_wound_flesh" "burstysquirt.prt"
+
+ "mtr_wound_flesh" "textures/decals/hurt02"
+ "mtr_wound_metal" "textures/decals/hurtmetal"
+ "mtr_splat_flesh" "textures/decals/dsplat2"
+ "mtr_splat_flesh2" "textures/decals/dsplat5"
+ "mtr_splat_flesh3" "textures/decals/dsplat7"
+ "mtr_splat_flesh4" "textures/decals/dsplat11"
+ "snd_flesh" "shotgun_impact_flesh"
+
+ "kick_time" "400"
+ "kick_amplitude" "1"
+
+ "dv_time" "100"
+}
+
+entityDef projectile_bullet_shotgun_double {
+ "spawnclass" "idProjectile"
+ "mins" "-1 -1 -1"
+ "maxs" "1 1 1"
+ "cone" "3"
+ //"model" "models/items/projectiles/rocket.ase"
+ "scale" "0.1"
+ "noshadows" "1"
+
+ "snd_flesh" "bullet_impact_flesh"
+ "snd_metal" "bullet_impact_metal"
+ "snd_stone" "bullet_impact_stone"
+ "snd_wood" "bullet_impact_wood"
+ "snd_cardboard" "bullet_impact_cardboard"
+ "snd_glass" "bullet_impact_glass"
+ "snd_liquid" "bullet_impact_liquid"
+ "snd_ricochet" "bullet_ricochet"
+ "snd_plastic" "small_plastic"
+
+ "snd_impact" "shotgun_impact_metal"
+
+ "def_damage" "damage_shotgun_double"
+
+ "launchFromBarrel" "0"
+ "health" "0"
+ "velocity" "7200 0 0" // .38 Special 600 fps. Source: Petzal, David E. "How fast is a speeding bullet." Field and Stream. 97 (1992): 23
+ "angular_velocity" "0 0 0"
+ "thrust" "0"
+ "thrust_start" "0"
+ "thrust_end" "0"
+ "linear_friction" "0"
+ "angular_friction" "0"
+ "contact_friction" "0"
+ "bounce" "0.6"
+ "mass" "1.0"
+ "gravity" "0"
+ "fuse" "4"
+
+ "detonate_on_fuse" "0"
+ "detonate_on_death" "0"
+ "detonate_on_world" "1"
+ "detonate_on_actor" "1"
+
+ "impact_damage_effect" "1"
+ "impact_gib" "1"
+
+ "mtr_detonate" "textures/decals/bulleth02"
+ "mtr_detonate_glass" "textures/decals/testbulletglass1"
+ "decal_size" "10"
+ "model_smokespark" "sgsmokeandspark.prt"
+ "model_ricochet" "bulletricochet.prt"
+
+ "model_slimesplash" "sludgebulletimpact.prt"
+
+ "smoke_fly" ""
+ "smoke_detonate" "smokeandspark.smoke"
+ "smoke_bounce" "sparkdebris.smoke"
+ "smoke_fuse" ""
+
+ "mtr_light_shader" ""
+ "light_color" "0 0 0"
+ "light_radius" "0"
+ "light_offset" "0 0 0"
+
+ "mtr_explode_light_shader" ""
+ "explode_light_color" "0 0 0"
+ "explode_light_radius" "0"
+ "explode_light_fadetime" "0"
+}
diff --git a/base/script/doom_main.script b/base/script/doom_main.script
new file mode 100644
index 00000000..f9e6d969
--- /dev/null
+++ b/base/script/doom_main.script
@@ -0,0 +1,92 @@
+/***********************************************************************
+
+doom_main.script
+
+This is the main script that is loaded before any level scripts load.
+
+***********************************************************************/
+
+// base defines and util functions
+#include "script/doom_defs.script"
+#include "script/doom_events.script"
+#include "script/doom_util.script"
+#include "script/weapon_base.script"
+#include "script/ai_base.script"
+
+// ai
+#include "script/ai_monster_base.script"
+#include "script/ai_monster_zombie_base.script"
+#include "script/ai_monster_demon_archvile.script"
+#include "script/ai_monster_demon_cherub.script"
+#include "script/ai_monster_demon_hellknight.script"
+#include "script/ai_monster_demon_imp.script"
+#include "script/ai_monster_demon_maggot.script"
+#include "script/ai_monster_demon_mancubus.script"
+#include "script/ai_monster_demon_pinky.script"
+#include "script/ai_monster_demon_revenant.script"
+#include "script/ai_monster_demon_trite.script"
+#include "script/ai_monster_demon_wraith.script"
+#include "script/ai_monster_flying_lostsoul.script"
+#include "script/ai_monster_flying_cacodemon.script"
+#include "script/ai_monster_zombie.script"
+#include "script/ai_monster_zombie_morgue.script"
+#include "script/ai_monster_zombie_sawyer.script"
+#include "script/ai_monster_zombie_bernie.script"
+#include "script/ai_monster_zombie_commando_cgun.script"
+#include "script/ai_monster_zombie_commando_tentacle.script"
+#include "script/ai_monster_zombie_security_pistol.script"
+#include "script/ai_monster_turret.script"
+#include "script/ai_monster_boss_vagary.script"
+#include "script/ai_monster_boss_cyberdemon.script"
+#include "script/ai_monster_boss_guardian.script"
+#include "script/ai_monster_boss_guardian_seeker.script"
+#include "script/ai_monster_boss_sabaoth.script"
+#include "script/ai_character.script"
+#include "script/ai_character_prone.script"
+#include "script/ai_character_sentry.script"
+#include "script/ai_player.script"
+
+// map specific character scripts
+#include "script/ai_alphalabs2_scientist1.script"
+
+// map scripts
+#include "script/map_admin1.script"
+#include "script/map_alphalabs1.script"
+#include "script/map_alphalabs2.script"
+#include "script/map_alphalabs3.script"
+#include "script/map_alphalabs4.script"
+#include "script/map_caves.script"
+#include "script/map_caves2.script"
+#include "script/map_comm1.script"
+#include "script/map_commoutside_lift.script"
+#include "script/map_commoutside.script"
+#include "script/map_cpu.script"
+#include "script/map_cpuboss.script"
+#include "script/map_delta1.script"
+#include "script/map_delta2a.script"
+#include "script/map_delta2b.script"
+#include "script/map_delta3.script"
+#include "script/map_delta5.script"
+#include "script/map_enpro.script"
+#include "script/map_hell1.script"
+#include "script/map_hellhole.script"
+#include "script/map_recycling1.script"
+#include "script/map_recycling2.script"
+#include "script/map_site3.script"
+#include "script/map_marscity1.script"
+#include "script/map_marscity2.script"
+#include "script/map_mc_underground.script"
+#include "script/map_monorail.script"
+
+// test scripts
+#include "script/ai_follower.script"
+
+void doom_main() {
+ sys.print( "Entering doom_main()\n");
+
+ //
+ // Do any script setup here
+ //
+
+ sys.print( "Exiting doom_main()\n" );
+}
diff --git a/base/script/weapon_base.script b/base/script/weapon_base.script
new file mode 100644
index 00000000..3803d344
--- /dev/null
+++ b/base/script/weapon_base.script
@@ -0,0 +1,111 @@
+/***********************************************************************
+
+weapon_base.script
+
+Base object for weapons. Contains variables required by game code.
+
+***********************************************************************/
+float global_flashlight_intensity;
+object weapon_base {
+ // required by game
+ boolean WEAPON_ATTACK;
+ boolean WEAPON_RELOAD;
+ boolean WEAPON_NETRELOAD;
+ boolean WEAPON_NETENDRELOAD;
+ boolean WEAPON_RAISEWEAPON;
+ boolean WEAPON_LOWERWEAPON;
+
+ boolean WEAPON_NETFIRING;
+
+
+
+ // required by player script.
+ boolean WEAPON_START_FIRING;
+
+ // called by game
+ void EnterCinematic();
+ void ExitCinematic();
+ void NetCatchup();
+ void WeaponStolen();
+ void OwnerDied();
+ void UpdateSkin();
+
+ // called by player script
+ string GetFireAnim();
+};
+
+/*
+=====================
+weapon_base::EnterCinematic
+
+Required by game code for cinematics. Implemented in subclasses.
+=====================
+*/
+void weapon_base::EnterCinematic() {
+}
+
+/*
+=====================
+weapon_base::ExitCinematic
+
+Required by game code for cinematics. Implemented in subclasses.
+=====================
+*/
+void weapon_base::ExitCinematic() {
+}
+
+/*
+=====================
+weapon_base::NetCatchup
+
+Required by game code for networking. Implemented in subclasses.
+=====================
+*/
+void weapon_base::NetCatchup() {
+
+}
+
+/*
+=====================
+weapon_base::WeaponStolen
+
+Required by game code for multiplayer when weapon is stolen. Implemented in subclasses.
+=====================
+*/
+void weapon_base::WeaponStolen() {
+}
+
+/*
+=====================
+weapon_base::OwnerDied
+
+Required by game code for when the weapon owner dies. Implemented in subclasses.
+Script is destroyed right after this is called, so any code must execute in a single frame.
+=====================
+*/
+void weapon_base::OwnerDied() {
+}
+
+/*
+=====================
+weapon_base::UpdateSkin
+
+Required by game code for when the skin needs to change (for example, when the player has a power up). Implemented in subclasses.
+Code is called is only guaranteed to execute for one frame, so any code should either set the weapon state or start a new thread.
+=====================
+*/
+void weapon_base::UpdateSkin() {
+
+}
+
+/*
+=====================
+weapon_base::GetFireAnim
+
+Used by the player script to get the fire anim. Subclasses can override this in order to sync the
+viewmodel with the player model.
+=====================
+*/
+string weapon_base::GetFireAnim() {
+ return "fire";
+}
diff --git a/neo/doom3/game.vcxproj b/neo/doom3/game.vcxproj
index 03b58186..cd700987 100644
--- a/neo/doom3/game.vcxproj
+++ b/neo/doom3/game.vcxproj
@@ -355,6 +355,7 @@ typeinfo.exe
+
@@ -406,6 +407,20 @@ typeinfo.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create
@@ -431,6 +446,7 @@ typeinfo.exe
+
@@ -482,6 +498,20 @@ typeinfo.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/neo/doom3/game.vcxproj.filters b/neo/doom3/game.vcxproj.filters
index 867300f3..1a594c70 100644
--- a/neo/doom3/game.vcxproj.filters
+++ b/neo/doom3/game.vcxproj.filters
@@ -16,6 +16,9 @@
{bf06073d-3e0f-4e0a-9acf-43c6e35a9af7}
+
+ {12aff2d0-4c6d-4e10-825f-fd89f869b61d}
+
@@ -168,6 +171,51 @@
GameSys
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ GameSys
+
@@ -302,6 +350,51 @@
GameSys
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ Weapons
+
+
+ GameSys
+
diff --git a/neo/doom3/game/Actor.cpp b/neo/doom3/game/Actor.cpp
index 90f6dab4..7103190c 100644
--- a/neo/doom3/game/Actor.cpp
+++ b/neo/doom3/game/Actor.cpp
@@ -2174,7 +2174,20 @@ void idActor::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir
gameLocal.Error( "Unknown damageDef '%s'", damageDefName );
}
- int damage = damageDef->GetInt( "damage" ) * damageScale;
+// jmarshall - added min/max damage support.
+ //int damage = damageDef->GetInt( "damage" ) * damageScale;
+ int minDamage = damageDef->GetInt("minDamage", "-1");
+ int maxDamage = damageDef->GetInt("maxDamage", "-1");
+ int damage = -1;
+ if (minDamage == -1 || maxDamage == -1) {
+ int damageBase = damageDef->GetInt("damage");
+ minDamage = damageBase - (damageBase * 0.2f);
+ maxDamage = damageBase + (damageBase * 0.2f);
+ }
+
+ damage = rvRandom::irand(minDamage, maxDamage) * damageScale;
+// jmarshall
+
damage = GetDamageForLocation( damage, location );
// inform the attacker that they hit someone
diff --git a/neo/doom3/game/Entity.h b/neo/doom3/game/Entity.h
index 607312c4..790f5f66 100644
--- a/neo/doom3/game/Entity.h
+++ b/neo/doom3/game/Entity.h
@@ -248,6 +248,8 @@ public:
bool GetMasterPosition( idVec3 &masterOrigin, idMat3 &masterAxis ) const;
void GetWorldVelocities( idVec3 &linearVelocity, idVec3 &angularVelocity ) const;
+ virtual void CallNativeEvent(idStr& name) {};
+
// physics
// set a new physics object to be used by this entity
void SetPhysics( idPhysics *phys );
@@ -351,6 +353,48 @@ public:
void ServerSendEvent( int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient ) const;
void ClientSendEvent( int eventId, const idBitMsg *msg ) const;
+ int GetIntKey(const char* key)
+ {
+ int value;
+ spawnArgs.GetInt(key, "0", value);
+ return value;
+ }
+
+ float GetFloatKey(const char* key)
+ {
+ float value;
+ spawnArgs.GetFloat(key, "0", value);
+ return value;
+ }
+
+ const char* GetKey(const char* key)
+ {
+ const char* value;
+
+ spawnArgs.GetString(key, "", &value);
+
+ return value;
+ }
+
+ float idEntity::GetFloat(const char* key)
+ {
+ return spawnArgs.GetFloat(key, "0");
+ }
+
+ int idEntity::GetInt(const char* key)
+ {
+ return spawnArgs.GetInt(key, "0");
+ }
+
+ bool idEntity::GetBool(const char* key)
+ {
+ return spawnArgs.GetBool(key, "0");
+ }
+
+ idVec3 idEntity::GetOrigin(void)
+ {
+ return GetLocalCoordinates(GetPhysics()->GetOrigin());
+ }
protected:
renderEntity_t renderEntity; // used to present a model to the renderer
int modelDefHandle; // handle to static renderer model
@@ -390,7 +434,7 @@ private:
void QuitTeam( void ); // leave the current team
void UpdatePVSAreas( void );
-
+public:
// events
void Event_GetName( void );
void Event_SetName( const char *name );
@@ -511,7 +555,7 @@ protected:
idAnimator animator;
damageEffect_t * damageEffects;
-private:
+public:
void Event_GetJointHandle( const char *jointname );
void Event_ClearAllJoints( void );
void Event_ClearJoint( jointHandle_t jointnum );
diff --git a/neo/doom3/game/Game_local.cpp b/neo/doom3/game/Game_local.cpp
index ef6f1d17..425519a2 100644
--- a/neo/doom3/game/Game_local.cpp
+++ b/neo/doom3/game/Game_local.cpp
@@ -2171,6 +2171,15 @@ gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
player = GetLocalPlayer();
+ for (int i = 0; i < delayRemoveEntities.Num(); i++)
+ {
+ if (gameLocal.time > delayRemoveEntities[i].removeTime)
+ {
+ delayRemoveEntities[i].entity->PostEventMS(&EV_Remove, 0);
+ delayRemoveEntities.RemoveIndex(i);
+ }
+ }
+
if ( !isMultiplayer && g_stopTime.GetBool() ) {
// clear any debug lines from a previous frame
gameRenderWorld->DebugClearLines( time + 1 );
@@ -4362,3 +4371,30 @@ void idGameLocal::GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] ) {
sprintf(gui, "guis/loadscreenbeta.gui");
}
+/*
+===============
+idGameLocal::DelayRemoveEntity
+===============
+*/
+void idGameLocal::DelayRemoveEntity(idEntity* entity, int delay)
+{
+ rvmGameDelayRemoveEntry_t entry;
+ entry.entity = entity;
+ entry.removeTime = gameLocal.time + delay;
+ delayRemoveEntities.Append(entry);
+}
+
+/*
+================
+idGameLocal::Spawn
+================
+*/
+idEntity* idGameLocal::Spawn(const char* classname)
+{
+ idEntity* ent;
+ idDict dict;
+
+ dict.Set("classname", classname);
+ gameLocal.SpawnEntityDef(dict, &ent);
+ return ent;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/Game_local.h b/neo/doom3/game/Game_local.h
index 9bfaa307..c1844288 100644
--- a/neo/doom3/game/Game_local.h
+++ b/neo/doom3/game/Game_local.h
@@ -60,6 +60,7 @@ extern idSoundWorld * gameSoundWorld;
// the "gameversion" client command will print this plus compile date
#define GAME_VERSION "baseDOOM-1"
+#define GAME_FRAMETIME 0.016 // 16 milliseconds
// classes used by idGameLocal
class idEntity;
@@ -238,6 +239,12 @@ private:
int spawnId;
};
+struct rvmGameDelayRemoveEntry_t
+{
+ int32_t removeTime;
+ idEntity* entity;
+};
+
//============================================================================
class idGameLocal : public idGame {
@@ -369,15 +376,27 @@ public:
// Initializes all map variables common to both save games and spawned games
void LoadMap( const char *mapName, int randseed );
+ void DelayRemoveEntity(idEntity* entity, int delay);
+
void LocalMapRestart( void );
void MapRestart( void );
static void MapRestart_f( const idCmdArgs &args );
+ float SysScriptTime(void) const
+ {
+ return MS2SEC(realClientTime);
+ }
+ float SysScriptFrameTime(void) const
+ {
+ return MS2SEC(time - previousTime);
+ }
bool NextMap( void ); // returns wether serverinfo settings have been modified
static void NextMap_f( const idCmdArgs &args );
idMapFile * GetLevelMap( void );
const char * GetMapName( void ) const;
+ idEntity* Spawn(const char* classname);
+
int NumAAS( void ) const;
idAAS * GetAAS( int num ) const;
idAAS * GetAAS( const char *name ) const;
@@ -572,6 +591,8 @@ private:
void UpdateLagometer( int aheadOfServer, int dupeUsercmds );
void GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] );
+
+ idList delayRemoveEntities;
};
//============================================================================
@@ -741,4 +762,19 @@ const int CINEMATIC_SKIP_DELAY = SEC2MS( 2.0f );
#include "script/Script_Interpreter.h"
#include "script/Script_Thread.h"
+#include "weapons/Weapon_fist.h"
+#include "weapons/Weapon_pistol.h"
+#include "weapons/Weapon_flashlight.h"
+#include "weapons/Weapon_pda.h"
+#include "weapons/Weapon_shotgun.h"
+#include "weapons/Weapon_double_shotgun.h"
+#include "weapons/Weapon_machinegun.h"
+#include "weapons/Weapon_plasmagun.h"
+#include "weapons/Weapon_chaingun.h"
+#include "weapons/Weapon_rocketlauncher.h"
+#include "weapons/Weapon_bfg.h"
+#include "weapons/Weapon_handgrenade.h"
+#include "weapons/Weapon_chainsaw.h"
+#include "weapons/Weapon_grabber.h"
+
#endif /* !__GAME_LOCAL_H__ */
diff --git a/neo/doom3/game/Player.h b/neo/doom3/game/Player.h
index 9aea9edf..a0bef353 100644
--- a/neo/doom3/game/Player.h
+++ b/neo/doom3/game/Player.h
@@ -674,6 +674,7 @@ private:
void Event_GetPreviousWeapon( void );
void Event_SelectWeapon( const char *weaponName );
void Event_GetWeaponEntity( void );
+public:
void Event_OpenPDA( void );
void Event_InPDA( void );
void Event_ExitTeleporter( void );
diff --git a/neo/doom3/game/PredictedValue.h b/neo/doom3/game/PredictedValue.h
new file mode 100644
index 00000000..551b4ce2
--- /dev/null
+++ b/neo/doom3/game/PredictedValue.h
@@ -0,0 +1,82 @@
+/*
+===========================================================================
+
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+
+This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
+
+Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Doom 3 BFG Edition Source Code. If not, see .
+
+In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
+
+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+
+===========================================================================
+*/
+#ifndef PREDICTED_VALUE_H_
+#define PREDICTED_VALUE_H_
+
+#include "Game_local.h"
+
+/*
+================================================
+A simple class to handle simple predictable values
+on multiplayer clients.
+
+The class encapsulates the actual value to be stored
+as well as the client frame number on which it is set.
+
+When reading predicted values from a snapshot, the actual
+value is only updated if the server has processed the client's
+usercmd for the frame in which the client predicted the value.
+Got that?
+================================================
+*/
+template< class type_ >
+class idPredictedValue
+{
+public:
+ explicit idPredictedValue();
+ explicit idPredictedValue( const type_ & value_ );
+
+ void Set( const type_ & newValue );
+
+ idPredictedValue< type_ >& operator=( const type_ & value );
+
+ idPredictedValue< type_ >& operator+=( const type_ & toAdd );
+ idPredictedValue< type_ >& operator-=( const type_ & toSubtract );
+
+ bool UpdateFromSnapshot( const type_ & valueFromSnapshot, int clientNumber );
+
+ type_ Get() const
+ {
+ return value;
+ }
+
+private:
+ // Noncopyable
+ idPredictedValue( const idPredictedValue< type_ >& other );
+ idPredictedValue< type_ >& operator=( const idPredictedValue< type_ >& other );
+
+ type_ value;
+ int clientPredictedMilliseconds; // The time in which the client predicted the value.
+
+ void UpdatePredictionTime();
+};
+
+
+
+
+#endif
diff --git a/neo/doom3/game/PredictedValue_impl.h b/neo/doom3/game/PredictedValue_impl.h
new file mode 100644
index 00000000..f10c0ef6
--- /dev/null
+++ b/neo/doom3/game/PredictedValue_impl.h
@@ -0,0 +1,237 @@
+/*
+===========================================================================
+
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+
+This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
+
+Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Doom 3 BFG Edition Source Code. If not, see .
+
+In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
+
+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
+
+===========================================================================
+*/
+#ifndef PREDICTED_VALUE_IMPL_H_
+#define PREDICTED_VALUE_IMPL_H_
+
+#include "PredictedValue.h"
+#include "Player.h"
+
+/*
+===============
+idPredictedValue::idPredictedValue
+===============
+*/
+template< class type_ >
+idPredictedValue< type_ >::idPredictedValue() :
+ value(),
+ clientPredictedMilliseconds( 0 )
+{
+}
+
+/*
+===============
+idPredictedValue::idPredictedValue
+===============
+*/
+template< class type_ >
+idPredictedValue< type_ >::idPredictedValue( const type_ & value_ ) :
+ value( value_ ),
+ clientPredictedMilliseconds( 0 )
+{
+}
+
+/*
+===============
+idPredictedValue::UpdatePredictionTime
+===============
+*/
+template< class type_ >
+void idPredictedValue< type_ >::UpdatePredictionTime()
+{
+ //if( gameLocal.GetLocalPlayer() != NULL )
+ //{
+ // clientPredictedMilliseconds = gameLocal.GetLocalPlayer()->usercmd.clientGameMilliseconds;
+ //}
+}
+
+/*
+===============
+idPredictedValue::Set
+===============
+*/
+template< class type_ >
+void idPredictedValue< type_ >::Set( const type_ & newValue )
+{
+ value = newValue;
+ UpdatePredictionTime();
+}
+
+/*
+===============
+idPredictedValue::operator=
+===============
+*/
+template< class type_ >
+idPredictedValue< type_ >& idPredictedValue< type_ >::operator=( const type_ & newValue )
+{
+ Set( newValue );
+ return *this;
+}
+
+/*
+===============
+idPredictedValue::operator+=
+===============
+*/
+template< class type_ >
+idPredictedValue< type_ >& idPredictedValue< type_ >::operator+=( const type_ & toAdd )
+{
+ Set( value + toAdd );
+ return *this;
+}
+
+/*
+===============
+idPredictedValue::operator-=
+===============
+*/
+template< class type_ >
+idPredictedValue< type_ >& idPredictedValue< type_ >::operator-=( const type_ & toSubtract )
+{
+ Set( value - toSubtract );
+ return *this;
+}
+
+/*
+===============
+idPredictedValue::UpdateFromSnapshot
+
+Always updates the value for remote clients.
+
+Only updates the actual value if the snapshot usercmd frame is newer than the one in which
+the client predicted this value.
+
+Returns true if the value was set, false if not.
+===============
+*/
+template< class type_ >
+bool idPredictedValue< type_ >::UpdateFromSnapshot( const type_ & valueFromSnapshot, int clientNumber )
+{
+ //if( clientNumber != gameLocal.GetLocalClientNum() )
+ //{
+ // value = valueFromSnapshot;
+ // return true;
+ //}
+ //
+ //if( gameLocal.GetLastClientUsercmdMilliseconds( clientNumber ) >= clientPredictedMilliseconds )
+ //{
+ // value = valueFromSnapshot;
+ // return true;
+ //}
+
+ return false;
+}
+
+/*
+===============
+operator==
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator==( const idPredictedValue< firstType_ >& lhs, const idPredictedValue< secondType_ >& rhs )
+{
+ return lhs.Get() == rhs.Get();
+}
+
+/*
+===============
+operator!=
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator!=( const idPredictedValue< firstType_ >& lhs, const idPredictedValue< secondType_ >& rhs )
+{
+ return lhs.Get() != rhs.Get();
+}
+
+/*
+===============
+operator==
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator==( const idPredictedValue< firstType_ >& lhs, const secondType_ & rhs )
+{
+ return lhs.Get() == rhs;
+}
+
+/*
+===============
+operator==
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator==( const firstType_ lhs, const idPredictedValue< secondType_ >& rhs )
+{
+ return lhs == rhs.Get();
+}
+
+/*
+===============
+operator!=
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator!=( const idPredictedValue< firstType_ >& lhs, const secondType_ & rhs )
+{
+ return lhs.Get() != rhs;
+}
+
+/*
+===============
+operator!=
+
+Overload for idPredictedValue.
+We only care if the values are equal, not the frame number.
+===============
+*/
+template< class firstType_, class secondType_ >
+bool operator!=( const firstType_ lhs, const idPredictedValue< secondType_ >& rhs )
+{
+ return lhs != rhs.Get();
+}
+
+
+
+
+#endif
diff --git a/neo/doom3/game/Weapon.cpp b/neo/doom3/game/Weapon.cpp
index 2845f931..e076ee4f 100644
--- a/neo/doom3/game/Weapon.cpp
+++ b/neo/doom3/game/Weapon.cpp
@@ -1,35 +1,114 @@
/*
===========================================================================
-IceTech GPL Source Code
-Copyright (C) 2026 Justin Marshall
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
+Copyright (C) 2016-2017 Dustin Land
-This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
+This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
-IceTech Source Code is free software: you can redistribute it and/or modify
+Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
-IceTech Source Code is distributed in the hope that it will be useful,
+Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with IceTech Source Code. If not, see .
+along with Doom 3 BFG Edition Source Code. If not, see .
-In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
+In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
-If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
-#include "precompiled.h"
#pragma hdrstop
-
+#include "precompiled.h"
#include "Game_local.h"
+#include "PredictedValue_impl.h"
+
+/***********************************************************************
+
+ rvmWeaponObject
+
+***********************************************************************/
+
+const idEventDef EV_Weapon_Grabber_SetGrabDistance("grabberGrabDistance", "f");
+
+CLASS_DECLARATION(idClass, rvmWeaponObject)
+EVENT(EV_Weapon_Grabber_SetGrabDistance, idWeapon::Event_GrabberSetGrabDistance)
+END_CLASS
+
+/*
+==================
+rvmWeaponObject::Init
+==================
+*/
+void rvmWeaponObject::Init(idWeapon* weapon)
+{
+ next_attack = 0.0f;
+ owner = weapon;
+ stateThread.SetOwner(this);
+ owner->Event_WeaponRising();
+
+ idClass::Init();
+}
+
+/*
+==================
+rvmWeaponObject::IsFiring
+==================
+*/
+bool rvmWeaponObject::IsFiring()
+{
+ if (IsStateRunning("Fire"))
+ {
+ return true;
+ }
+
+ if (next_attack >= gameLocal.realClientTime)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+==================
+rvmWeaponObject::IsReloading
+==================
+*/
+bool rvmWeaponObject::IsReloading()
+{
+ if (IsStateRunning("Reload"))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+==================
+rvmWeaponObject::FindSound
+==================
+*/
+const idSoundShader* rvmWeaponObject::FindSound(const char* name)
+{
+ const char* soundName = owner->GetKey(name);
+ if (soundName == NULL || soundName[0] == 0)
+ {
+ return NULL;
+ }
+ return declManager->FindSound(soundName);
+}
+
/***********************************************************************
@@ -37,274 +116,20 @@ If you have questions concerning this license or the applicable additional terms
***********************************************************************/
-//
-// event defs
-//
-const idEventDef EV_Weapon_Clear("");
-const idEventDef EV_Weapon_GetOwner("getOwner", NULL, 'e');
-const idEventDef EV_Weapon_Next("nextWeapon");
-const idEventDef EV_Weapon_State("weaponState", "sd");
-const idEventDef EV_Weapon_UseAmmo("useAmmo", "d");
-const idEventDef EV_Weapon_AddToClip("addToClip", "d");
-const idEventDef EV_Weapon_AmmoInClip("ammoInClip", NULL, 'f');
-const idEventDef EV_Weapon_AmmoAvailable("ammoAvailable", NULL, 'f');
-const idEventDef EV_Weapon_TotalAmmoCount("totalAmmoCount", NULL, 'f');
-const idEventDef EV_Weapon_ClipSize("clipSize", NULL, 'f');
-const idEventDef EV_Weapon_WeaponOutOfAmmo("weaponOutOfAmmo");
-const idEventDef EV_Weapon_WeaponReady("weaponReady");
-const idEventDef EV_Weapon_WeaponReloading("weaponReloading");
-const idEventDef EV_Weapon_WeaponHolstered("weaponHolstered");
-const idEventDef EV_Weapon_WeaponRising("weaponRising");
-const idEventDef EV_Weapon_WeaponLowering("weaponLowering");
-const idEventDef EV_Weapon_Flashlight("flashlight", "d");
-const idEventDef EV_Weapon_LaunchProjectiles("launchProjectiles", "dffff");
-const idEventDef EV_Weapon_CreateProjectile("createProjectile", NULL, 'e');
-const idEventDef EV_Weapon_EjectBrass("ejectBrass");
-const idEventDef EV_Weapon_Melee("melee", NULL, 'd');
-const idEventDef EV_Weapon_GetWorldModel("getWorldModel", NULL, 'e');
-const idEventDef EV_Weapon_AllowDrop("allowDrop", "d");
-const idEventDef EV_Weapon_AutoReload("autoReload", NULL, 'f');
-const idEventDef EV_Weapon_NetReload("netReload");
-const idEventDef EV_Weapon_IsInvisible("isInvisible", NULL, 'f');
-const idEventDef EV_Weapon_NetEndReload("netEndReload");
-
//
// class def
//
CLASS_DECLARATION(idAnimatedEntity, idWeapon)
-EVENT(EV_Weapon_Clear, idWeapon::Event_Clear)
-EVENT(EV_Weapon_GetOwner, idWeapon::Event_GetOwner)
-EVENT(EV_Weapon_State, idWeapon::Event_WeaponState)
-EVENT(EV_Weapon_WeaponReady, idWeapon::Event_WeaponReady)
-EVENT(EV_Weapon_WeaponOutOfAmmo, idWeapon::Event_WeaponOutOfAmmo)
-EVENT(EV_Weapon_WeaponReloading, idWeapon::Event_WeaponReloading)
-EVENT(EV_Weapon_WeaponHolstered, idWeapon::Event_WeaponHolstered)
-EVENT(EV_Weapon_WeaponRising, idWeapon::Event_WeaponRising)
-EVENT(EV_Weapon_WeaponLowering, idWeapon::Event_WeaponLowering)
-EVENT(EV_Weapon_UseAmmo, idWeapon::Event_UseAmmo)
-EVENT(EV_Weapon_AddToClip, idWeapon::Event_AddToClip)
-EVENT(EV_Weapon_AmmoInClip, idWeapon::Event_AmmoInClip)
-EVENT(EV_Weapon_AmmoAvailable, idWeapon::Event_AmmoAvailable)
-EVENT(EV_Weapon_TotalAmmoCount, idWeapon::Event_TotalAmmoCount)
-EVENT(EV_Weapon_ClipSize, idWeapon::Event_ClipSize)
-EVENT(AI_PlayAnim, idWeapon::Event_PlayAnim)
-EVENT(AI_PlayCycle, idWeapon::Event_PlayCycle)
-EVENT(AI_SetBlendFrames, idWeapon::Event_SetBlendFrames)
-EVENT(AI_GetBlendFrames, idWeapon::Event_GetBlendFrames)
-EVENT(AI_AnimDone, idWeapon::Event_AnimDone)
-EVENT(EV_Weapon_Next, idWeapon::Event_Next)
-EVENT(EV_SetSkin, idWeapon::Event_SetSkin)
-EVENT(EV_Weapon_Flashlight, idWeapon::Event_Flashlight)
-EVENT(EV_Light_GetLightParm, idWeapon::Event_GetLightParm)
-EVENT(EV_Light_SetLightParm, idWeapon::Event_SetLightParm)
-EVENT(EV_Light_SetLightParms, idWeapon::Event_SetLightParms)
-EVENT(EV_Weapon_LaunchProjectiles, idWeapon::Event_LaunchProjectiles)
-EVENT(EV_Weapon_CreateProjectile, idWeapon::Event_CreateProjectile)
-EVENT(EV_Weapon_EjectBrass, idWeapon::Event_EjectBrass)
-EVENT(EV_Weapon_Melee, idWeapon::Event_Melee)
-EVENT(EV_Weapon_GetWorldModel, idWeapon::Event_GetWorldModel)
-EVENT(EV_Weapon_AllowDrop, idWeapon::Event_AllowDrop)
-EVENT(EV_Weapon_AutoReload, idWeapon::Event_AutoReload)
-EVENT(EV_Weapon_NetReload, idWeapon::Event_NetReload)
-EVENT(EV_Weapon_IsInvisible, idWeapon::Event_IsInvisible)
-EVENT(EV_Weapon_NetEndReload, idWeapon::Event_NetEndReload)
END_CLASS
-/*
-================
-Weapon fire muzzle light
+idCVar cg_projectile_clientAuthoritative_maxCatchup("cg_projectile_clientAuthoritative_maxCatchup", "500", CVAR_INTEGER, "");
-This is intentionally separate from idWeapon::MuzzleFlashLight(). The old
-MuzzleFlashLight path is used by weapon scripts as a flashlight / persistent
-weapon light, so projectile firing gets its own short-lived light here.
-================
-*/
-struct weaponFireMuzzleLightLocal_t {
- int viewHandle;
- int worldHandle;
- int startTime;
- int endTime;
- int duration;
- idVec3 baseColor;
- float baseRadius;
- renderLight_t viewLight;
- renderLight_t worldLight;
-};
+idCVar g_useWeaponDepthHack("g_useWeaponDepthHack", "1", CVAR_BOOL, "Crunch z depth on weapons");
-static weaponFireMuzzleLightLocal_t weaponFireMuzzleLights[MAX_GENTITIES];
-static bool weaponFireMuzzleLightsInitialized = false;
+idCVar g_weaponShadows("g_weaponShadows", "0", CVAR_BOOL | CVAR_ARCHIVE, "Cast shadows from weapons");
-static void InitWeaponFireMuzzleLights(void) {
- if (weaponFireMuzzleLightsInitialized) {
- return;
- }
-
- for (int i = 0; i < MAX_GENTITIES; i++) {
- memset(&weaponFireMuzzleLights[i], 0, sizeof(weaponFireMuzzleLights[i]));
- weaponFireMuzzleLights[i].viewHandle = -1;
- weaponFireMuzzleLights[i].worldHandle = -1;
- }
-
- weaponFireMuzzleLightsInitialized = true;
-}
-
-static void FreeWeaponFireMuzzleLight(int entityNum) {
- InitWeaponFireMuzzleLights();
-
- if (entityNum < 0 || entityNum >= MAX_GENTITIES) {
- return;
- }
-
- weaponFireMuzzleLightLocal_t& fireLight = weaponFireMuzzleLights[entityNum];
-
- if (fireLight.viewHandle != -1) {
- gameRenderWorld->FreeLightDef(fireLight.viewHandle);
- fireLight.viewHandle = -1;
- }
- if (fireLight.worldHandle != -1) {
- gameRenderWorld->FreeLightDef(fireLight.worldHandle);
- fireLight.worldHandle = -1;
- }
-
- fireLight.startTime = 0;
- fireLight.endTime = 0;
- fireLight.duration = 0;
- fireLight.baseColor.Zero();
- fireLight.baseRadius = 0.0f;
- memset(&fireLight.viewLight, 0, sizeof(fireLight.viewLight));
- memset(&fireLight.worldLight, 0, sizeof(fireLight.worldLight));
-}
-
-static void StartWeaponFireMuzzleLight(int entityNum, int ownerEntityNum, const idVec3& viewOrigin, const idMat3& viewAxis, const idVec3& worldOrigin, const idMat3& worldAxis, const idMaterial* shader, const idVec3& color, float radius, int duration, float diversity) {
- InitWeaponFireMuzzleLights();
-
- if (entityNum < 0 || entityNum >= MAX_GENTITIES) {
- return;
- }
- if (radius <= 0.0f || duration <= 0) {
- return;
- }
-
- weaponFireMuzzleLightLocal_t& fireLight = weaponFireMuzzleLights[entityNum];
-
- memset(&fireLight.viewLight, 0, sizeof(fireLight.viewLight));
- memset(&fireLight.worldLight, 0, sizeof(fireLight.worldLight));
-
- fireLight.startTime = gameLocal.time;
- fireLight.endTime = gameLocal.time + duration;
- fireLight.duration = duration;
- fireLight.baseColor = color;
- fireLight.baseRadius = radius * 5;
-
- fireLight.viewLight.pointLight = true;
- fireLight.viewLight.noShadows = true;
- fireLight.viewLight.shader = shader;
- fireLight.viewLight.origin = viewOrigin;
- fireLight.viewLight.axis = viewAxis;
- fireLight.viewLight.lightRadius[0] = radius;
- fireLight.viewLight.lightRadius[1] = radius;
- fireLight.viewLight.lightRadius[2] = radius;
- fireLight.viewLight.shaderParms[SHADERPARM_RED] = color.x;
- fireLight.viewLight.shaderParms[SHADERPARM_GREEN] = color.y;
- fireLight.viewLight.shaderParms[SHADERPARM_BLUE] = color.z;
- fireLight.viewLight.shaderParms[SHADERPARM_ALPHA] = 1.0f;
- fireLight.viewLight.shaderParms[SHADERPARM_TIMESCALE] = 1.0f;
- fireLight.viewLight.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
- fireLight.viewLight.shaderParms[SHADERPARM_DIVERSITY] = diversity;
-
- // Only the owning player should see the view-model light. This is what
- // makes the light appear attached to the first-person muzzle flash joint.
- if (ownerEntityNum >= 0) {
- fireLight.viewLight.allowLightInViewID = ownerEntityNum + 1;
- // Keep this separate from the old LIGHTID_VIEW_MUZZLE_FLASH path used by
- // MuzzleFlashLight/flashlight so the two systems never fight each other.
- fireLight.viewLight.lightId = LIGHTID_VIEW_MUZZLE_FLASH + ownerEntityNum + 256;
- }
-
- fireLight.worldLight = fireLight.viewLight;
- fireLight.worldLight.origin = worldOrigin;
- fireLight.worldLight.axis = worldAxis;
- fireLight.worldLight.allowLightInViewID = 0;
- if (ownerEntityNum >= 0) {
- fireLight.worldLight.suppressLightInViewID = ownerEntityNum + 1;
- fireLight.worldLight.lightId = LIGHTID_WORLD_MUZZLE_FLASH + ownerEntityNum + 256;
- }
-
- if (fireLight.viewHandle != -1) {
- gameRenderWorld->UpdateLightDef(fireLight.viewHandle, &fireLight.viewLight);
- }
- else {
- fireLight.viewHandle = gameRenderWorld->AddLightDef(&fireLight.viewLight);
- }
-
- if (fireLight.worldHandle != -1) {
- gameRenderWorld->UpdateLightDef(fireLight.worldHandle, &fireLight.worldLight);
- }
- else {
- fireLight.worldHandle = gameRenderWorld->AddLightDef(&fireLight.worldLight);
- }
-}
-
-static bool UpdateWeaponFireMuzzleLight(int entityNum, const idVec3& viewOrigin, const idMat3& viewAxis, const idVec3& worldOrigin, const idMat3& worldAxis) {
- InitWeaponFireMuzzleLights();
-
- if (entityNum < 0 || entityNum >= MAX_GENTITIES) {
- return false;
- }
-
- weaponFireMuzzleLightLocal_t& fireLight = weaponFireMuzzleLights[entityNum];
-
- if (fireLight.viewHandle == -1 && fireLight.worldHandle == -1) {
- return false;
- }
-
- if (gameLocal.time >= fireLight.endTime || fireLight.duration <= 0) {
- FreeWeaponFireMuzzleLight(entityNum);
- return false;
- }
-
- float frac = (float)(fireLight.endTime - gameLocal.time) / (float)fireLight.duration;
- if (frac < 0.0f) {
- frac = 0.0f;
- }
- else if (frac > 1.0f) {
- frac = 1.0f;
- }
-
- // A squared falloff gives a hot first frame and then a quick natural fade.
- float fade = frac * frac;
- float radius = fireLight.baseRadius * (0.35f + 0.65f * fade);
-
- fireLight.viewLight.origin = viewOrigin;
- fireLight.viewLight.axis = viewAxis;
- fireLight.viewLight.lightRadius[0] = radius;
- fireLight.viewLight.lightRadius[1] = radius;
- fireLight.viewLight.lightRadius[2] = radius;
- fireLight.viewLight.shaderParms[SHADERPARM_RED] = fireLight.baseColor.x * fade;
- fireLight.viewLight.shaderParms[SHADERPARM_GREEN] = fireLight.baseColor.y * fade;
- fireLight.viewLight.shaderParms[SHADERPARM_BLUE] = fireLight.baseColor.z * fade;
- fireLight.viewLight.shaderParms[SHADERPARM_ALPHA] = fade;
-
- fireLight.worldLight.origin = worldOrigin;
- fireLight.worldLight.axis = worldAxis;
- fireLight.worldLight.lightRadius[0] = radius;
- fireLight.worldLight.lightRadius[1] = radius;
- fireLight.worldLight.lightRadius[2] = radius;
- fireLight.worldLight.shaderParms[SHADERPARM_RED] = fireLight.baseColor.x * fade;
- fireLight.worldLight.shaderParms[SHADERPARM_GREEN] = fireLight.baseColor.y * fade;
- fireLight.worldLight.shaderParms[SHADERPARM_BLUE] = fireLight.baseColor.z * fade;
- fireLight.worldLight.shaderParms[SHADERPARM_ALPHA] = fade;
-
- if (fireLight.viewHandle != -1) {
- gameRenderWorld->UpdateLightDef(fireLight.viewHandle, &fireLight.viewLight);
- }
- if (fireLight.worldHandle != -1) {
- gameRenderWorld->UpdateLightDef(fireLight.worldHandle, &fireLight.worldLight);
- }
-
- return true;
-}
+extern idCVar cg_predictedSpawn_debug;
/***********************************************************************
@@ -317,11 +142,17 @@ static bool UpdateWeaponFireMuzzleLight(int entityNum, const idVec3& viewOrigin,
idWeapon::idWeapon()
================
*/
-idWeapon::idWeapon() {
+idWeapon::idWeapon()
+{
owner = NULL;
worldModel = NULL;
weaponDef = NULL;
- thread = NULL;
+
+ isFiring = false;
+ isLinked = false;
+ currentWeaponObject = nullptr;
+ isFlashLight = false;
+ OutOfAmmo = false;
memset(&guiLight, 0, sizeof(guiLight));
memset(&muzzleFlash, 0, sizeof(muzzleFlash));
@@ -335,11 +166,16 @@ idWeapon::idWeapon() {
guiLightHandle = -1;
nozzleGlowHandle = -1;
modelDefHandle = -1;
+ grabberState = -1;
berserk = 2;
brassDelay = 0;
allowDrop = true;
+ isPlayerFlashlight = false;
+
+ fraccos = 0.0f;
+ fraccos2 = 0.0f;
Clear();
@@ -351,7 +187,8 @@ idWeapon::idWeapon() {
idWeapon::~idWeapon()
================
*/
-idWeapon::~idWeapon() {
+idWeapon::~idWeapon()
+{
Clear();
delete worldModel.GetEntity();
}
@@ -362,16 +199,23 @@ idWeapon::~idWeapon() {
idWeapon::Spawn
================
*/
-void idWeapon::Spawn(void) {
- if (!gameLocal.isClient) {
+void idWeapon::Spawn()
+{
+ if (!gameLocal.isClient)
+ {
// setup the world model
worldModel = static_cast(gameLocal.SpawnEntityType(idAnimatedEntity::Type, NULL));
worldModel.GetEntity()->fl.networkSync = true;
}
- thread = new idThread();
- thread->ManualDelete();
- thread->ManualControl();
+ if (1 /*!common->IsMultiplayer()*/)
+ {
+ //grabber.Initialize();
+ }
+
+ //thread = new idThread();
+ //thread->ManualDelete();
+ //thread->ManualControl();
}
/*
@@ -381,16 +225,39 @@ idWeapon::SetOwner
Only called at player spawn time, not each weapon switch
================
*/
-void idWeapon::SetOwner(idPlayer* _owner) {
+void idWeapon::SetOwner(idPlayer* _owner)
+{
assert(!owner);
owner = _owner;
SetName(va("%s_weapon", owner->name.c_str()));
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->SetName(va("%s_weapon_worldmodel", owner->name.c_str()));
}
}
+/*
+================
+idWeapon::SetFlashlightOwner
+
+Only called at player spawn time, not each weapon switch
+================
+*/
+void idWeapon::SetFlashlightOwner(idPlayer* _owner)
+{
+ assert(!owner);
+ owner = _owner;
+ SetName(va("%s_weapon_flashlight", owner->name.c_str()));
+
+ isFlashLight = true;
+
+ if (worldModel.GetEntity())
+ {
+ worldModel.GetEntity()->SetName(va("%s_weapon_flashlight_worldmodel", owner->name.c_str()));
+ }
+}
+
/*
================
idWeapon::ShouldConstructScriptObjectAtSpawn
@@ -399,7 +266,8 @@ Called during idEntity::Spawn to see if it should construct the script object or
Overridden by subclasses that need to spawn the script object themselves.
================
*/
-bool idWeapon::ShouldConstructScriptObjectAtSpawn(void) const {
+bool idWeapon::ShouldConstructScriptObjectAtSpawn() const
+{
return false;
}
@@ -408,7 +276,8 @@ bool idWeapon::ShouldConstructScriptObjectAtSpawn(void) const {
idWeapon::CacheWeapon
================
*/
-void idWeapon::CacheWeapon(const char* weaponName) {
+void idWeapon::CacheWeapon(const char* weaponName)
+{
const idDeclEntityDef* weaponDef;
const char* brassDefName;
const char* clipModelName;
@@ -416,17 +285,21 @@ void idWeapon::CacheWeapon(const char* weaponName) {
const char* guiName;
weaponDef = gameLocal.FindEntityDef(weaponName, false);
- if (!weaponDef) {
+ if (!weaponDef)
+ {
return;
}
// precache the brass collision model
brassDefName = weaponDef->dict.GetString("def_ejectBrass");
- if (brassDefName[0]) {
+ if (brassDefName[0])
+ {
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(brassDefName, false);
- if (brassDef) {
+ if (brassDef)
+ {
brassDef->dict.GetString("clipmodel", "", &clipModelName);
- if (!clipModelName[0]) {
+ if (!clipModelName[0])
+ {
clipModelName = brassDef->dict.GetString("model"); // use the visual model
}
// load the trace model
@@ -435,7 +308,8 @@ void idWeapon::CacheWeapon(const char* weaponName) {
}
guiName = weaponDef->dict.GetString("gui");
- if (guiName[0]) {
+ if (guiName[0])
+ {
uiManager->FindGui(guiName, true, false, true);
}
}
@@ -445,15 +319,18 @@ void idWeapon::CacheWeapon(const char* weaponName) {
idWeapon::Save
================
*/
-void idWeapon::Save(idSaveGame* savefile) const {
+void idWeapon::Save(idSaveGame* savefile) const
+{
+ // jmarshall
+ //savefile->WriteInt( status );
+ //savefile->WriteObject( thread );
+ //savefile->WriteString( state );
+ //savefile->WriteString( idealState );
+ // jmarshall end
- savefile->WriteInt(status);
- savefile->WriteObject(thread);
- savefile->WriteString(state);
- savefile->WriteString(idealState);
savefile->WriteInt(animBlendFrames);
savefile->WriteInt(animDoneTime);
- savefile->WriteBool(isLinked);
+ //savefile->WriteBool( isLinked );
savefile->WriteObject(owner);
worldModel.Save(savefile);
@@ -485,6 +362,9 @@ void idWeapon::Save(idSaveGame* savefile) const {
savefile->WriteString(meleeDefName);
savefile->WriteInt(brassDelay);
savefile->WriteString(icon);
+ savefile->WriteString(pdaIcon);
+ savefile->WriteString(displayName);
+ savefile->WriteString(itemDesc);
savefile->WriteInt(guiLightHandle);
savefile->WriteRenderLight(guiLight);
@@ -511,7 +391,7 @@ void idWeapon::Save(idSaveGame* savefile) const {
savefile->WriteInt(ammoType);
savefile->WriteInt(ammoRequired);
savefile->WriteInt(clipSize);
- savefile->WriteInt(ammoClip);
+ savefile->WriteInt(ammoClip.Get());
savefile->WriteInt(lowAmmo);
savefile->WriteBool(powerAmmo);
@@ -564,6 +444,38 @@ void idWeapon::Save(idSaveGame* savefile) const {
savefile->WriteBool(allowDrop);
savefile->WriteObject(projectileEnt);
+ //savefile->WriteStaticObject(grabber);
+ savefile->WriteInt(grabberState);
+
+ savefile->WriteJoint(smokeJointView);
+
+ savefile->WriteInt(weaponParticles.Num());
+ for (int i = 0; i < weaponParticles.Num(); i++)
+ {
+ WeaponParticle_t* part = weaponParticles.GetIndex(i);
+ savefile->WriteString(part->name);
+ savefile->WriteString(part->particlename);
+ savefile->WriteBool(part->active);
+ savefile->WriteInt(part->startTime);
+ savefile->WriteJoint(part->joint);
+ savefile->WriteBool(part->smoke);
+ if (!part->smoke)
+ {
+ savefile->WriteObject(part->emitter);
+ }
+ }
+ savefile->WriteInt(weaponLights.Num());
+ for (int i = 0; i < weaponLights.Num(); i++)
+ {
+ WeaponLight_t* light = weaponLights.GetIndex(i);
+ savefile->WriteString(light->name);
+ savefile->WriteBool(light->active);
+ savefile->WriteInt(light->startTime);
+ savefile->WriteJoint(light->joint);
+ savefile->WriteInt(light->lightHandle);
+ savefile->WriteRenderLight(light->light);
+ }
+
}
/*
@@ -571,24 +483,18 @@ void idWeapon::Save(idSaveGame* savefile) const {
idWeapon::Restore
================
*/
-void idWeapon::Restore(idRestoreGame* savefile) {
+void idWeapon::Restore(idRestoreGame* savefile)
+{
+ // jmarshall
+ //savefile->ReadInt( (int &)status );
+ //savefile->ReadObject( reinterpret_cast( thread ) );
+ //savefile->ReadString( state );
+ //savefile->ReadString( idealState );
+ // jmarshall end
- savefile->ReadInt((int&)status);
- savefile->ReadObject(reinterpret_cast(thread));
- savefile->ReadString(state);
- savefile->ReadString(idealState);
savefile->ReadInt(animBlendFrames);
savefile->ReadInt(animDoneTime);
- savefile->ReadBool(isLinked);
-
- // Re-link script fields
- WEAPON_ATTACK.LinkTo(scriptObject, "WEAPON_ATTACK");
- WEAPON_RELOAD.LinkTo(scriptObject, "WEAPON_RELOAD");
- WEAPON_NETRELOAD.LinkTo(scriptObject, "WEAPON_NETRELOAD");
- WEAPON_NETENDRELOAD.LinkTo(scriptObject, "WEAPON_NETENDRELOAD");
- WEAPON_NETFIRING.LinkTo(scriptObject, "WEAPON_NETFIRING");
- WEAPON_RAISEWEAPON.LinkTo(scriptObject, "WEAPON_RAISEWEAPON");
- WEAPON_LOWERWEAPON.LinkTo(scriptObject, "WEAPON_LOWERWEAPON");
+ //savefile->ReadBool( isLinked );
savefile->ReadObject(reinterpret_cast(owner));
worldModel.Restore(savefile);
@@ -621,18 +527,22 @@ void idWeapon::Restore(idRestoreGame* savefile) {
meleeDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_melee"), false);
const idDeclEntityDef* projectileDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_projectile"), false);
- if (projectileDef) {
+ if (projectileDef)
+ {
projectileDict = projectileDef->dict;
}
- else {
+ else
+ {
projectileDict.Clear();
}
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(weaponDef->dict.GetString("def_ejectBrass"), false);
- if (brassDef) {
+ if (brassDef)
+ {
brassDict = brassDef->dict;
}
- else {
+ else
+ {
brassDict.Clear();
}
@@ -640,15 +550,30 @@ void idWeapon::Restore(idRestoreGame* savefile) {
savefile->ReadString(meleeDefName);
savefile->ReadInt(brassDelay);
savefile->ReadString(icon);
+ savefile->ReadString(pdaIcon);
+ savefile->ReadString(displayName);
+ savefile->ReadString(itemDesc);
savefile->ReadInt(guiLightHandle);
savefile->ReadRenderLight(guiLight);
+ if (guiLightHandle >= 0)
+ {
+ guiLightHandle = gameRenderWorld->AddLightDef(&guiLight);
+ }
savefile->ReadInt(muzzleFlashHandle);
savefile->ReadRenderLight(muzzleFlash);
+ if (muzzleFlashHandle >= 0)
+ {
+ muzzleFlashHandle = gameRenderWorld->AddLightDef(&muzzleFlash);
+ }
savefile->ReadInt(worldMuzzleFlashHandle);
savefile->ReadRenderLight(worldMuzzleFlash);
+ if (worldMuzzleFlashHandle >= 0)
+ {
+ worldMuzzleFlashHandle = gameRenderWorld->AddLightDef(&worldMuzzleFlash);
+ }
savefile->ReadVec3(flashColor);
savefile->ReadInt(muzzleFlashEnd);
@@ -666,7 +591,11 @@ void idWeapon::Restore(idRestoreGame* savefile) {
savefile->ReadInt((int&)ammoType);
savefile->ReadInt(ammoRequired);
savefile->ReadInt(clipSize);
- savefile->ReadInt(ammoClip);
+
+ int savedAmmoClip = 0;
+ savefile->ReadInt(savedAmmoClip);
+ ammoClip = savedAmmoClip;
+
savefile->ReadInt(lowAmmo);
savefile->ReadBool(powerAmmo);
@@ -706,6 +635,10 @@ void idWeapon::Restore(idRestoreGame* savefile) {
savefile->ReadInt(nozzleGlowHandle);
savefile->ReadRenderLight(nozzleGlow);
+ if (nozzleGlowHandle >= 0)
+ {
+ nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
+ }
savefile->ReadVec3(nozzleGlowColor);
savefile->ReadMaterial(nozzleGlowShader);
@@ -719,6 +652,64 @@ void idWeapon::Restore(idRestoreGame* savefile) {
savefile->ReadBool(allowDrop);
savefile->ReadObject(reinterpret_cast(projectileEnt));
+
+ //savefile->ReadStaticObject(grabber);
+ savefile->ReadInt(grabberState);
+
+ savefile->ReadJoint(smokeJointView);
+
+ int particleCount;
+ savefile->ReadInt(particleCount);
+ for (int i = 0; i < particleCount; i++)
+ {
+ WeaponParticle_t newParticle;
+ memset(&newParticle, 0, sizeof(newParticle));
+
+ idStr name, particlename;
+ savefile->ReadString(name);
+ savefile->ReadString(particlename);
+
+ strcpy(newParticle.name, name.c_str());
+ strcpy(newParticle.particlename, particlename.c_str());
+
+ savefile->ReadBool(newParticle.active);
+ savefile->ReadInt(newParticle.startTime);
+ savefile->ReadJoint(newParticle.joint);
+ savefile->ReadBool(newParticle.smoke);
+ if (newParticle.smoke)
+ {
+ newParticle.particle = static_cast(declManager->FindType(DECL_PARTICLE, particlename, false));
+ }
+ else
+ {
+ savefile->ReadObject(reinterpret_cast(newParticle.emitter));
+ }
+
+ weaponParticles.Set(newParticle.name, newParticle);
+ }
+
+ int lightCount;
+ savefile->ReadInt(lightCount);
+ for (int i = 0; i < lightCount; i++)
+ {
+ WeaponLight_t newLight;
+ memset(&newLight, 0, sizeof(newLight));
+
+ idStr name;
+ savefile->ReadString(name);
+ strcpy(newLight.name, name.c_str());
+
+ savefile->ReadBool(newLight.active);
+ savefile->ReadInt(newLight.startTime);
+ savefile->ReadJoint(newLight.joint);
+ savefile->ReadInt(newLight.lightHandle);
+ savefile->ReadRenderLight(newLight.light);
+ if (newLight.lightHandle >= 0)
+ {
+ newLight.lightHandle = gameRenderWorld->AddLightDef(&newLight.light);
+ }
+ weaponLights.Set(newLight.name, newLight);
+ }
}
/***********************************************************************
@@ -732,38 +723,33 @@ void idWeapon::Restore(idRestoreGame* savefile) {
idWeapon::Clear
================
*/
-void idWeapon::Clear(void) {
- CancelEvents(&EV_Weapon_Clear);
-
+void idWeapon::Clear()
+{
DeconstructScriptObject();
scriptObject.Free();
- WEAPON_ATTACK.Unlink();
- WEAPON_RELOAD.Unlink();
- WEAPON_NETRELOAD.Unlink();
- WEAPON_NETENDRELOAD.Unlink();
- WEAPON_NETFIRING.Unlink();
- WEAPON_RAISEWEAPON.Unlink();
- WEAPON_LOWERWEAPON.Unlink();
-
- if (muzzleFlashHandle != -1) {
+ if (muzzleFlashHandle != -1)
+ {
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
muzzleFlashHandle = -1;
}
- if (muzzleFlashHandle != -1) {
+ if (muzzleFlashHandle != -1)
+ {
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
muzzleFlashHandle = -1;
}
- if (worldMuzzleFlashHandle != -1) {
+ if (worldMuzzleFlashHandle != -1)
+ {
gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
worldMuzzleFlashHandle = -1;
}
- FreeWeaponFireMuzzleLight(entityNumber);
- if (guiLightHandle != -1) {
+ if (guiLightHandle != -1)
+ {
gameRenderWorld->FreeLightDef(guiLightHandle);
guiLightHandle = -1;
}
- if (nozzleGlowHandle != -1) {
+ if (nozzleGlowHandle != -1)
+ {
gameRenderWorld->FreeLightDef(nozzleGlowHandle);
nozzleGlowHandle = -1;
}
@@ -785,7 +771,8 @@ void idWeapon::Clear(void) {
renderEntity.shaderParms[6] = 0.0f;
renderEntity.shaderParms[7] = 0.0f;
- if (refSound.referenceSound) {
+ if (refSound.referenceSound)
+ {
refSound.referenceSound->Free(true);
}
memset(&refSound, 0, sizeof(refSound_t));
@@ -793,14 +780,16 @@ void idWeapon::Clear(void) {
// setting diversity to 0 results in no random sound. -1 indicates random.
refSound.diversity = -1.0f;
- if (owner) {
+ if (owner)
+ {
// don't spatialize the weapon sounds
refSound.listenerId = owner->GetListenerId();
}
// clear out the sounds from our spawnargs since we'll copy them from the weapon def
const idKeyValue* kv = spawnArgs.MatchPrefix("snd_");
- while (kv) {
+ while (kv)
+ {
spawnArgs.Delete(kv->GetKey());
kv = spawnArgs.MatchPrefix("snd_");
}
@@ -824,6 +813,9 @@ void idWeapon::Clear(void) {
nextStrikeFx = 0;
icon = "";
+ pdaIcon = "";
+ displayName = "";
+ itemDesc = "";
playerViewAxis.Identity();
playerViewOrigin.Zero();
@@ -833,9 +825,6 @@ void idWeapon::Clear(void) {
muzzleOrigin.Zero();
pushVelocity.Zero();
- status = WP_HOLSTERED;
- state = "";
- idealState = "";
animBlendFrames = 0;
animDoneTime = 0;
@@ -845,10 +834,19 @@ void idWeapon::Clear(void) {
meleeDistance = 0.0f;
brassDict.Clear();
+ if (currentWeaponObject != nullptr)
+ {
+ delete currentWeaponObject;
+ currentWeaponObject = nullptr;
+ }
+
flashTime = 250;
lightOn = false;
silent_fire = false;
+ grabberState = -1;
+ //grabber.Update(owner, true);
+
ammoType = 0;
ammoRequired = 0;
ammoClip = 0;
@@ -874,6 +872,34 @@ void idWeapon::Clear(void) {
flashJointWorld = INVALID_JOINT;
ejectJointWorld = INVALID_JOINT;
+ smokeJointView = INVALID_JOINT;
+
+ //Clean up the weapon particles
+ for (int i = 0; i < weaponParticles.Num(); i++)
+ {
+ WeaponParticle_t* part = weaponParticles.GetIndex(i);
+ if (!part->smoke)
+ {
+ if (part->emitter != NULL)
+ {
+ //Destroy the emitters
+ part->emitter->PostEventMS(&EV_Remove, 0);
+ }
+ }
+ }
+ weaponParticles.Clear();
+
+ //Clean up the weapon lights
+ for (int i = 0; i < weaponLights.Num(); i++)
+ {
+ WeaponLight_t* light = weaponLights.GetIndex(i);
+ if (light->lightHandle != -1)
+ {
+ gameRenderWorld->FreeLightDef(light->lightHandle);
+ }
+ }
+ weaponLights.Clear();
+
hasBloodSplat = false;
nozzleFx = false;
nozzleFxFade = 1500;
@@ -896,7 +922,7 @@ void idWeapon::Clear(void) {
sndHum = NULL;
- isLinked = false;
+ //isLinked = false;
projectileEnt = NULL;
isFiring = false;
@@ -907,7 +933,8 @@ void idWeapon::Clear(void) {
idWeapon::InitWorldModel
================
*/
-void idWeapon::InitWorldModel(const idDeclEntityDef* def) {
+void idWeapon::InitWorldModel(const idDeclEntityDef* def)
+{
idEntity* ent;
ent = worldModel.GetEntity();
@@ -919,10 +946,12 @@ void idWeapon::InitWorldModel(const idDeclEntityDef* def) {
const char* attach = def->dict.GetString("joint_attach");
ent->SetSkin(NULL);
- if (model[0] && attach[0]) {
+ if (model[0] && attach[0])
+ {
ent->Show();
ent->SetModel(model);
- if (ent->GetAnimator()->ModelDef()) {
+ if (ent->GetAnimator()->ModelDef())
+ {
ent->SetSkin(ent->GetAnimator()->ModelDef()->GetDefaultSkin());
}
ent->GetPhysics()->SetContents(0);
@@ -931,15 +960,22 @@ void idWeapon::InitWorldModel(const idDeclEntityDef* def) {
ent->GetPhysics()->SetOrigin(vec3_origin);
ent->GetPhysics()->SetAxis(mat3_identity);
+ // We don't want to interpolate the world model of weapons, let them
+ // just bind normally to the player's joint and be driven by the player's
+ // animation so that the weapon and the player don't appear out of sync.
+ //ent->SetUseClientInterpolation( false );
+
// supress model in player views, but allow it in mirrors and remote views
renderEntity_t* worldModelRenderEntity = ent->GetRenderEntity();
- if (worldModelRenderEntity) {
+ if (worldModelRenderEntity)
+ {
worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber + 1;
worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber + 1;
worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
}
}
- else {
+ else
+ {
ent->SetModel("");
ent->Hide();
}
@@ -954,7 +990,8 @@ void idWeapon::InitWorldModel(const idDeclEntityDef* def) {
idWeapon::GetWeaponDef
================
*/
-void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
+void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip)
+{
const char* shader;
const char* objectType;
const char* vmodel;
@@ -966,7 +1003,8 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
Clear();
- if (!objectname || !objectname[0]) {
+ if (!objectname || !objectname[0])
+ {
return;
}
@@ -980,6 +1018,10 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
lowAmmo = weaponDef->dict.GetInt("lowAmmo");
icon = weaponDef->dict.GetString("icon");
+ pdaIcon = weaponDef->dict.GetString("pdaIcon");
+ displayName = weaponDef->dict.GetString("display_name");
+ itemDesc = weaponDef->dict.GetString("inv_desc");
+
silent_fire = weaponDef->dict.GetBool("silent_fire");
powerAmmo = weaponDef->dict.GetBool("powerAmmo");
@@ -993,20 +1035,24 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
// muzzle smoke
smokeName = weaponDef->dict.GetString("smoke_muzzle");
- if (*smokeName != '\0') {
+ if (*smokeName != '\0')
+ {
weaponSmoke = static_cast(declManager->FindType(DECL_PARTICLE, smokeName));
}
- else {
+ else
+ {
weaponSmoke = NULL;
}
continuousSmoke = weaponDef->dict.GetBool("continuousSmoke");
weaponSmokeStartTime = (continuousSmoke) ? gameLocal.time : 0;
smokeName = weaponDef->dict.GetString("smoke_strike");
- if (*smokeName != '\0') {
+ if (*smokeName != '\0')
+ {
strikeSmoke = static_cast(declManager->FindType(DECL_PARTICLE, smokeName));
}
- else {
+ else
+ {
strikeSmoke = NULL;
}
strikeSmokeStartTime = 0;
@@ -1017,7 +1063,8 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
// setup gui light
memset(&guiLight, 0, sizeof(guiLight));
const char* guiLightShader = weaponDef->dict.GetString("mtr_guiLightShader");
- if (*guiLightShader != '\0') {
+ if (*guiLightShader != '\0')
+ {
guiLight.shader = declManager->FindMaterial(guiLightShader, false);
guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = 3;
guiLight.pointLight = true;
@@ -1032,7 +1079,8 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
// copy the sounds from the weapon view model def into out spawnargs
const idKeyValue* kv = weaponDef->dict.MatchPrefix("snd_");
- while (kv) {
+ while (kv)
+ {
spawnArgs.Set(kv->GetKey(), kv->GetValue());
kv = weaponDef->dict.MatchPrefix("snd_", kv);
}
@@ -1044,22 +1092,37 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
guiLightJointView = animator.GetJointHandle("guiLight");
ventLightJointView = animator.GetJointHandle("ventLight");
+ idStr smokeJoint = weaponDef->dict.GetString("smoke_joint");
+ if (smokeJoint.Length() > 0)
+ {
+ smokeJointView = animator.GetJointHandle(smokeJoint);
+ }
+ else
+ {
+ smokeJointView = INVALID_JOINT;
+ }
+
// get the projectile
projectileDict.Clear();
projectileName = weaponDef->dict.GetString("def_projectile");
- if (projectileName[0] != '\0') {
+ if (projectileName[0] != '\0')
+ {
const idDeclEntityDef* projectileDef = gameLocal.FindEntityDef(projectileName, false);
- if (!projectileDef) {
+ if (!projectileDef)
+ {
gameLocal.Warning("Unknown projectile '%s' in weapon '%s'", projectileName, objectname);
}
- else {
+ else
+ {
const char* spawnclass = projectileDef->dict.GetString("spawnclass");
idTypeInfo* cls = idClass::GetClass(spawnclass);
- if (!cls || !cls->IsType(idProjectile::Type)) {
+ if (!cls || !cls->IsType(idProjectile::Type))
+ {
gameLocal.Warning("Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname);
}
- else {
+ else
+ {
projectileDict = projectileDef->dict;
}
}
@@ -1102,7 +1165,8 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
muzzleFlash.lightRadius[1] = flashRadius;
muzzleFlash.lightRadius[2] = flashRadius;
- if (!flashPointLight) {
+ if (!flashPointLight)
+ {
muzzleFlash.target = flashTarget;
muzzleFlash.up = flashUp;
muzzleFlash.right = flashRight;
@@ -1127,9 +1191,11 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
// get the melee damage def
meleeDistance = weaponDef->dict.GetFloat("melee_distance");
meleeDefName = weaponDef->dict.GetString("def_melee");
- if (meleeDefName.Length()) {
+ if (meleeDefName.Length())
+ {
meleeDef = gameLocal.FindEntityDef(meleeDefName, false);
- if (!meleeDef) {
+ if (!meleeDef)
+ {
gameLocal.Error("Unknown melee '%s'", meleeDefName.c_str());
}
}
@@ -1139,33 +1205,42 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
brassDelay = weaponDef->dict.GetInt("ejectBrassDelay", "0");
brassDefName = weaponDef->dict.GetString("def_ejectBrass");
- if (brassDefName[0]) {
+ if (brassDefName[0])
+ {
const idDeclEntityDef* brassDef = gameLocal.FindEntityDef(brassDefName, false);
- if (!brassDef) {
+ if (!brassDef)
+ {
gameLocal.Warning("Unknown brass '%s'", brassDefName);
}
- else {
+ else
+ {
brassDict = brassDef->dict;
}
}
- if ((ammoType < 0) || (ammoType >= AMMO_NUMTYPES)) {
+ if ((ammoType < 0) || (ammoType >= AMMO_NUMTYPES))
+ {
gameLocal.Warning("Unknown ammotype in object '%s'", objectname);
}
ammoClip = ammoinclip;
- if ((ammoClip < 0) || (ammoClip > clipSize)) {
+ if ((ammoClip.Get() < 0) || (ammoClip.Get() > clipSize))
+ {
// first time using this weapon so have it fully loaded to start
ammoClip = clipSize;
ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
- if (ammoClip > ammoAvail) {
+ if (ammoClip.Get() > ammoAvail)
+ {
ammoClip = ammoAvail;
}
+ //In D3XP we use ammo as soon as it is moved into the clip. This allows for weapons that share ammo
+ owner->inventory.UseAmmo(ammoType, ammoClip.Get());
}
renderEntity.gui[0] = NULL;
guiName = weaponDef->dict.GetString("gui");
- if (guiName[0]) {
+ if (guiName[0])
+ {
renderEntity.gui[0] = uiManager->FindGui(guiName, true, false, true);
}
@@ -1179,38 +1254,128 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
weaponOffsetTime = weaponDef->dict.GetFloat("weaponOffsetTime", "400");
weaponOffsetScale = weaponDef->dict.GetFloat("weaponOffsetScale", "0.005");
- if (!weaponDef->dict.GetString("weapon_scriptobject", NULL, &objectType)) {
- gameLocal.Error("No 'weapon_scriptobject' set on '%s'.", objectname);
+ if (!weaponDef->dict.GetString("weapon_class", NULL, &objectType))
+ {
+ gameLocal.Error("No 'weaponclass' set on '%s'.", objectname);
}
// setup script object
- if (!scriptObject.SetType(objectType)) {
- gameLocal.Error("Script object '%s' not found on weapon '%s'.", objectType, objectname);
+ idTypeInfo* typeInfo;
+ typeInfo = idClass::GetClass(objectType);
+ if (!typeInfo)
+ {
+ gameLocal.Error("Failed to get weapon class");
}
-
- WEAPON_ATTACK.LinkTo(scriptObject, "WEAPON_ATTACK");
- WEAPON_RELOAD.LinkTo(scriptObject, "WEAPON_RELOAD");
- WEAPON_NETRELOAD.LinkTo(scriptObject, "WEAPON_NETRELOAD");
- WEAPON_NETENDRELOAD.LinkTo(scriptObject, "WEAPON_NETENDRELOAD");
- WEAPON_NETFIRING.LinkTo(scriptObject, "WEAPON_NETFIRING");
- WEAPON_RAISEWEAPON.LinkTo(scriptObject, "WEAPON_RAISEWEAPON");
- WEAPON_LOWERWEAPON.LinkTo(scriptObject, "WEAPON_LOWERWEAPON");
+ if (currentWeaponObject != nullptr)
+ {
+ delete currentWeaponObject;
+ currentWeaponObject = nullptr;
+ }
+ currentWeaponObject = static_cast(typeInfo->CreateInstance());
+ currentWeaponObject->Init(this);
+ currentWeaponObject->CallSpawn();
spawnArgs = weaponDef->dict;
shader = spawnArgs.GetString("snd_hum");
- if (shader && *shader) {
+ if (shader && *shader)
+ {
sndHum = declManager->FindSound(shader);
StartSoundShader(sndHum, SND_CHANNEL_BODY, 0, false, NULL);
}
isLinked = true;
+ //isLinked = true;
+
// call script object's constructor
- ConstructScriptObject();
+ //ConstructScriptObject();
// make sure we have the correct skin
UpdateSkin();
+
+ //Initialize the particles
+ if (!gameLocal.isMultiplayer)
+ {
+
+ const idKeyValue* pkv = weaponDef->dict.MatchPrefix("weapon_particle", NULL);
+ while (pkv)
+ {
+ WeaponParticle_t newParticle;
+ memset(&newParticle, 0, sizeof(newParticle));
+
+ idStr name = pkv->GetValue();
+
+ strcpy(newParticle.name, name.c_str());
+
+ idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
+ newParticle.joint = animator.GetJointHandle(jointName.c_str());
+ newParticle.smoke = weaponDef->dict.GetBool(va("%s_smoke", name.c_str()));
+ newParticle.active = false;
+ newParticle.startTime = 0;
+
+ idStr particle = weaponDef->dict.GetString(va("%s_particle", name.c_str()));
+ strcpy(newParticle.particlename, particle.c_str());
+
+ if (newParticle.smoke)
+ {
+ newParticle.particle = static_cast(declManager->FindType(DECL_PARTICLE, particle, false));
+ }
+ else
+ {
+ idDict args;
+
+ const idDeclEntityDef* emitterDef = gameLocal.FindEntityDef("func_emitter", false);
+ args = emitterDef->dict;
+ args.Set("model", particle.c_str());
+ args.SetBool("start_off", true);
+
+ idEntity* ent;
+ gameLocal.SpawnEntityDef(args, &ent, false);
+ newParticle.emitter = (idFuncEmitter*)ent;
+
+ if (newParticle.emitter != NULL)
+ {
+ newParticle.emitter->BecomeActive(TH_THINK);
+ }
+ }
+
+ weaponParticles.Set(name.c_str(), newParticle);
+
+ pkv = weaponDef->dict.MatchPrefix("weapon_particle", pkv);
+ }
+
+ const idKeyValue* lkv = weaponDef->dict.MatchPrefix("weapon_light", NULL);
+ while (lkv)
+ {
+ WeaponLight_t newLight;
+ memset(&newLight, 0, sizeof(newLight));
+
+ newLight.lightHandle = -1;
+ newLight.active = false;
+ newLight.startTime = 0;
+
+ idStr name = lkv->GetValue();
+ strcpy(newLight.name, name.c_str());
+
+ idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
+ newLight.joint = animator.GetJointHandle(jointName.c_str());
+
+ shader = weaponDef->dict.GetString(va("%s_shader", name.c_str()));
+ newLight.light.shader = declManager->FindMaterial(shader, false);
+
+ float radius = weaponDef->dict.GetFloat(va("%s_radius", name.c_str()));
+ newLight.light.lightRadius[0] = newLight.light.lightRadius[1] = newLight.light.lightRadius[2] = radius;
+ newLight.light.pointLight = true;
+ newLight.light.noShadows = true;
+
+ newLight.light.allowLightInViewID = owner->entityNumber + 1;
+
+ weaponLights.Set(name.c_str(), newLight);
+
+ lkv = weaponDef->dict.MatchPrefix("weapon_light", lkv);
+ }
+ }
}
/***********************************************************************
@@ -1224,25 +1389,60 @@ void idWeapon::GetWeaponDef(const char* objectname, int ammoinclip) {
idWeapon::Icon
================
*/
-const char* idWeapon::Icon(void) const {
+const char* idWeapon::Icon() const
+{
return icon;
}
+/*
+================
+idWeapon::PdaIcon
+================
+*/
+const char* idWeapon::PdaIcon() const
+{
+ return pdaIcon;
+}
+
+/*
+================
+idWeapon::DisplayName
+================
+*/
+const char* idWeapon::DisplayName() const
+{
+ return ""; // idLocalization::GetString(displayName);
+}
+
+/*
+================
+idWeapon::Description
+================
+*/
+const char* idWeapon::Description() const
+{
+ return "";// idLocalization::GetString(itemDesc);
+}
+
/*
================
idWeapon::UpdateGUI
================
*/
-void idWeapon::UpdateGUI(void) {
- if (!renderEntity.gui[0]) {
+void idWeapon::UpdateGUI()
+{
+ if (!renderEntity.gui[0])
+ {
return;
}
- if (status == WP_HOLSTERED) {
- return;
- }
+ //if( status == WP_HOLSTERED )
+ //{
+ // return;
+ //}
- if (owner->weaponGone) {
+ if (owner->weaponGone)
+ {
// dropping weapons was implemented wierd, so we have to not update the gui when it happens or we'll get a negative ammo count
return;
}
@@ -1263,20 +1463,29 @@ void idWeapon::UpdateGUI(void) {
int inclip = AmmoInClip();
int ammoamount = AmmoAvailable();
- if (ammoamount < 0) {
+ if (ammoamount < 0)
+ {
// show infinite ammo
renderEntity.gui[0]->SetStateString("player_ammo", "");
}
- else {
+ else
+ {
// show remaining ammo
- renderEntity.gui[0]->SetStateString("player_totalammo", va("%i", ammoamount - inclip));
+ renderEntity.gui[0]->SetStateString("player_totalammo", va("%i", ammoamount));
renderEntity.gui[0]->SetStateString("player_ammo", ClipSize() ? va("%i", inclip) : "--");
renderEntity.gui[0]->SetStateString("player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--");
- renderEntity.gui[0]->SetStateString("player_allammo", va("%i/%i", inclip, ammoamount - inclip));
+
+ renderEntity.gui[0]->SetStateString("player_allammo", va("%i/%i", inclip, ammoamount));
}
renderEntity.gui[0]->SetStateBool("player_ammo_empty", (ammoamount == 0));
renderEntity.gui[0]->SetStateBool("player_clip_empty", (inclip == 0));
renderEntity.gui[0]->SetStateBool("player_clip_low", (inclip <= lowAmmo));
+
+ //Let the HUD know the total amount of ammo regardless of the ammo required value
+ renderEntity.gui[0]->SetStateString("player_ammo_count", va("%i", AmmoCount()));
+
+ //Grabber Gui Info
+ renderEntity.gui[0]->SetStateString("grabber_state", va("%i", grabberState));
}
/***********************************************************************
@@ -1290,7 +1499,8 @@ void idWeapon::UpdateGUI(void) {
idWeapon::UpdateFlashPosition
================
*/
-void idWeapon::UpdateFlashPosition(void) {
+void idWeapon::UpdateFlashPosition()
+{
// the flash has an explicit joint for locating it
GetGlobalJointTransform(true, flashJointView, muzzleFlash.origin, muzzleFlash.axis);
@@ -1311,13 +1521,16 @@ void idWeapon::UpdateFlashPosition(void) {
idWeapon::MuzzleFlashLight
================
*/
-void idWeapon::MuzzleFlashLight(void) {
+void idWeapon::MuzzleFlashLight()
+{
- if (!lightOn && (!g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0])) {
+ if (!lightOn && (!g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0]))
+ {
return;
}
- if (flashJointView == INVALID_JOINT) {
+ if (flashJointView == INVALID_JOINT)
+ {
return;
}
@@ -1333,11 +1546,13 @@ void idWeapon::MuzzleFlashLight(void) {
// the light will be removed at this time
muzzleFlashEnd = gameLocal.time + flashTime;
- if (muzzleFlashHandle != -1) {
+ if (muzzleFlashHandle != -1)
+ {
gameRenderWorld->UpdateLightDef(muzzleFlashHandle, &muzzleFlash);
gameRenderWorld->UpdateLightDef(worldMuzzleFlashHandle, &worldMuzzleFlash);
}
- else {
+ else
+ {
muzzleFlashHandle = gameRenderWorld->AddLightDef(&muzzleFlash);
worldMuzzleFlashHandle = gameRenderWorld->AddLightDef(&worldMuzzleFlash);
}
@@ -1348,44 +1563,103 @@ void idWeapon::MuzzleFlashLight(void) {
idWeapon::UpdateSkin
================
*/
-bool idWeapon::UpdateSkin(void) {
- const function_t* func;
-
- if (!isLinked) {
- return false;
- }
-
- func = scriptObject.GetFunction("UpdateSkin");
- if (!func) {
- common->Warning("Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName());
- return false;
- }
-
- // use the frameCommandThread since it's safe to use outside of framecommands
- gameLocal.frameCommandThread->CallFunction(this, func, true);
- gameLocal.frameCommandThread->Execute();
+bool idWeapon::UpdateSkin()
+{
+ //const function_t *func;
+ //
+ //if ( !isLinked ) {
+ // return false;
+ //}
+ //
+ //func = scriptObject.GetFunction( "UpdateSkin" );
+ //if ( !func ) {
+ // idLib::Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
+ // return false;
+ //}
+ //
+ //// use the frameCommandThread since it's safe to use outside of framecommands
+ //gameLocal.frameCommandThread->CallFunction( this, func, true );
+ //gameLocal.frameCommandThread->Execute();
return true;
}
+
+/*
+================
+idWeapon::FlashlightOn
+================
+*/
+void idWeapon::FlashlightOn()
+{
+ //const function_t *func;
+ //
+ //if ( !isLinked ) {
+ // return;
+ //}
+ //
+ //func = scriptObject.GetFunction( "TurnOn" );
+ //if ( !func ) {
+ // idLib::Warning( "Can't find function 'TurnOn' in object '%s'", scriptObject.GetTypeName() );
+ // return;
+ //}
+ //
+ //// use the frameCommandThread since it's safe to use outside of framecommands
+ //gameLocal.frameCommandThread->CallFunction( this, func, true );
+ //gameLocal.frameCommandThread->Execute();
+
+ return;
+}
+
+
+/*
+================
+idWeapon::FlashlightOff
+================
+*/
+void idWeapon::FlashlightOff()
+{
+ //const function_t *func;
+ //
+ //if ( !isLinked ) {
+ // return;
+ //}
+ //
+ //func = scriptObject.GetFunction( "TurnOff" );
+ //if ( !func ) {
+ // idLib::Warning( "Can't find function 'TurnOff' in object '%s'", scriptObject.GetTypeName() );
+ // return;
+ //}
+ //
+ //// use the frameCommandThread since it's safe to use outside of framecommands
+ //gameLocal.frameCommandThread->CallFunction( this, func, true );
+ //gameLocal.frameCommandThread->Execute();
+ //
+ //return;
+}
+
/*
================
idWeapon::SetModel
================
*/
-void idWeapon::SetModel(const char* modelname) {
+void idWeapon::SetModel(const char* modelname)
+{
assert(modelname);
- if (modelDefHandle >= 0) {
+ if (modelDefHandle >= 0)
+ {
gameRenderWorld->RemoveDecals(modelDefHandle);
}
renderEntity.hModel = animator.SetModel(modelname);
- if (renderEntity.hModel) {
+ if (renderEntity.hModel)
+ {
renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
animator.GetJoints(&renderEntity.numJoints, &renderEntity.joints);
}
- else {
+ else
+ {
renderEntity.customSkin = NULL;
renderEntity.callback = NULL;
renderEntity.numJoints = 0;
@@ -1403,18 +1677,23 @@ idWeapon::GetGlobalJointTransform
This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
================
*/
-bool idWeapon::GetGlobalJointTransform(bool viewModel, const jointHandle_t jointHandle, idVec3& offset, idMat3& axis) {
- if (viewModel) {
+bool idWeapon::GetGlobalJointTransform(bool viewModel, const jointHandle_t jointHandle, idVec3& offset, idMat3& axis)
+{
+ if (viewModel)
+ {
// view model
- if (animator.GetJointTransform(jointHandle, gameLocal.time, offset, axis)) {
+ if (animator.GetJointTransform(jointHandle, gameLocal.time, offset, axis))
+ {
offset = offset * viewWeaponAxis + viewWeaponOrigin;
axis = axis * viewWeaponAxis;
return true;
}
}
- else {
+ else
+ {
// world model
- if (worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform(jointHandle, gameLocal.time, offset, axis)) {
+ if (worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform(jointHandle, gameLocal.time, offset, axis))
+ {
offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis();
axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis();
return true;
@@ -1430,7 +1709,8 @@ bool idWeapon::GetGlobalJointTransform(bool viewModel, const jointHandle_t joint
idWeapon::SetPushVelocity
================
*/
-void idWeapon::SetPushVelocity(const idVec3& pushVelocity) {
+void idWeapon::SetPushVelocity(const idVec3& pushVelocity)
+{
this->pushVelocity = pushVelocity;
}
@@ -1446,8 +1726,17 @@ void idWeapon::SetPushVelocity(const idVec3& pushVelocity) {
idWeapon::Think
================
*/
-void idWeapon::Think(void) {
- // do nothing because the present is called from the player through PresentWeapon
+void idWeapon::Think()
+{
+ if (isFlashLight)
+ {
+ return;
+ }
+
+ if (currentWeaponObject)
+ {
+ currentWeaponObject->Execute();
+ }
}
/*
@@ -1455,10 +1744,10 @@ void idWeapon::Think(void) {
idWeapon::Raise
================
*/
-void idWeapon::Raise(void) {
- if (isLinked) {
- WEAPON_RAISEWEAPON = true;
- }
+void idWeapon::Raise()
+{
+ currentWeaponObject->SetState("Raise");
+ currentWeaponObject->AppendState("Idle");
}
/*
@@ -1466,11 +1755,10 @@ void idWeapon::Raise(void) {
idWeapon::PutAway
================
*/
-void idWeapon::PutAway(void) {
+void idWeapon::PutAway()
+{
hasBloodSplat = false;
- if (isLinked) {
- WEAPON_LOWERWEAPON = true;
- }
+ currentWeaponObject->SetState("Lower");
}
/*
@@ -1479,10 +1767,13 @@ idWeapon::Reload
NOTE: this is only for impulse-triggered reload, auto reload is scripted
================
*/
-void idWeapon::Reload(void) {
- if (isLinked) {
- WEAPON_RELOAD = true;
- }
+void idWeapon::Reload()
+{
+ OutOfAmmo = false;
+
+ // Reload and go back to idle.
+ currentWeaponObject->SetState("Reload");
+ currentWeaponObject->AppendState("Idle");
}
/*
@@ -1490,14 +1781,18 @@ void idWeapon::Reload(void) {
idWeapon::LowerWeapon
================
*/
-void idWeapon::LowerWeapon(void) {
- if (!hide) {
+void idWeapon::LowerWeapon()
+{
+ if (!hide)
+ {
hideStart = 0.0f;
hideEnd = hideDistance;
- if (gameLocal.time - hideStartTime < hideTime) {
+ if (gameLocal.time - hideStartTime < hideTime)
+ {
hideStartTime = gameLocal.time - (hideTime - (gameLocal.time - hideStartTime));
}
- else {
+ else
+ {
hideStartTime = gameLocal.time;
}
hide = true;
@@ -1509,16 +1804,20 @@ void idWeapon::LowerWeapon(void) {
idWeapon::RaiseWeapon
================
*/
-void idWeapon::RaiseWeapon(void) {
+void idWeapon::RaiseWeapon()
+{
Show();
- if (hide) {
+ if (hide)
+ {
hideStart = hideDistance;
hideEnd = 0.0f;
- if (gameLocal.time - hideStartTime < hideTime) {
+ if (gameLocal.time - hideStartTime < hideTime)
+ {
hideStartTime = gameLocal.time - (hideTime - (gameLocal.time - hideStartTime));
}
- else {
+ else
+ {
hideStartTime = gameLocal.time;
}
hide = false;
@@ -1530,13 +1829,14 @@ void idWeapon::RaiseWeapon(void) {
idWeapon::HideWeapon
================
*/
-void idWeapon::HideWeapon(void) {
+void idWeapon::HideWeapon()
+{
Hide();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->Hide();
}
muzzleFlashEnd = 0;
- FreeWeaponFireMuzzleLight(entityNumber);
}
/*
@@ -1544,12 +1844,15 @@ void idWeapon::HideWeapon(void) {
idWeapon::ShowWeapon
================
*/
-void idWeapon::ShowWeapon(void) {
+void idWeapon::ShowWeapon()
+{
Show();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->Show();
}
- if (lightOn) {
+ if (lightOn)
+ {
MuzzleFlashLight();
}
}
@@ -1559,8 +1862,10 @@ void idWeapon::ShowWeapon(void) {
idWeapon::HideWorldModel
================
*/
-void idWeapon::HideWorldModel(void) {
- if (worldModel.GetEntity()) {
+void idWeapon::HideWorldModel()
+{
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->Hide();
}
}
@@ -1570,8 +1875,10 @@ void idWeapon::HideWorldModel(void) {
idWeapon::ShowWorldModel
================
*/
-void idWeapon::ShowWorldModel(void) {
- if (worldModel.GetEntity()) {
+void idWeapon::ShowWorldModel()
+{
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->Show();
}
}
@@ -1581,20 +1888,15 @@ void idWeapon::ShowWorldModel(void) {
idWeapon::OwnerDied
================
*/
-void idWeapon::OwnerDied(void) {
- if (isLinked) {
- SetState("OwnerDied", 0);
- thread->Execute();
- }
+void idWeapon::OwnerDied()
+{
+ currentWeaponObject->OwnerDied();
Hide();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->Hide();
}
-
- // don't clear the weapon immediately since the owner might have killed himself by firing the weapon
- // within the current stack frame
- PostEventMS(&EV_Weapon_Clear, 0);
}
/*
@@ -1602,21 +1904,27 @@ void idWeapon::OwnerDied(void) {
idWeapon::BeginAttack
================
*/
-void idWeapon::BeginAttack(void) {
- if (status != WP_OUTOFAMMO) {
+void idWeapon::BeginAttack()
+{
+ if (!OutOfAmmo)
+ {
lastAttack = gameLocal.time;
}
- if (!isLinked) {
+ if (currentWeaponObject->IsFiring())
+ {
return;
}
- if (!WEAPON_ATTACK) {
- if (sndHum) {
- StopSound(SND_CHANNEL_BODY, false);
- }
+ if (currentWeaponObject->IsReloading())
+ {
+ return;
}
- WEAPON_ATTACK = true;
+
+ currentWeaponObject->SetState("Fire");
+ currentWeaponObject->AppendState("Idle");
+
+ isFiring = true;
}
/*
@@ -1624,16 +1932,9 @@ void idWeapon::BeginAttack(void) {
idWeapon::EndAttack
================
*/
-void idWeapon::EndAttack(void) {
- if (!WEAPON_ATTACK.IsLinked()) {
- return;
- }
- if (WEAPON_ATTACK) {
- WEAPON_ATTACK = false;
- if (sndHum) {
- StartSoundShader(sndHum, SND_CHANNEL_BODY, 0, false, NULL);
- }
- }
+void idWeapon::EndAttack()
+{
+ isFiring = false;
}
/*
@@ -1641,8 +1942,9 @@ void idWeapon::EndAttack(void) {
idWeapon::isReady
================
*/
-bool idWeapon::IsReady(void) const {
- return !hide && !IsHidden() && ((status == WP_RELOAD) || (status == WP_READY) || (status == WP_OUTOFAMMO));
+bool idWeapon::IsReady() const
+{
+ return !hide && !IsHidden() && !currentWeaponObject->IsRunning() && !IsHolstered() && !currentWeaponObject->IsStateRunning("Lower");
}
/*
@@ -1650,8 +1952,13 @@ bool idWeapon::IsReady(void) const {
idWeapon::IsReloading
================
*/
-bool idWeapon::IsReloading(void) const {
- return (status == WP_RELOAD);
+bool idWeapon::IsReloading() const
+{
+ if (!currentWeaponObject)
+ {
+ return false;
+ }
+ return currentWeaponObject->IsStateRunning("Reload");
}
/*
@@ -1659,8 +1966,13 @@ bool idWeapon::IsReloading(void) const {
idWeapon::IsHolstered
================
*/
-bool idWeapon::IsHolstered(void) const {
- return (status == WP_HOLSTERED);
+bool idWeapon::IsHolstered() const
+{
+ if (!currentWeaponObject)
+ {
+ return false;
+ }
+ return currentWeaponObject->IsStateRunning("Holstered");
}
/*
@@ -1668,8 +1980,11 @@ bool idWeapon::IsHolstered(void) const {
idWeapon::ShowCrosshair
================
*/
-bool idWeapon::ShowCrosshair(void) const {
- return !(state == idStr(WP_RISING) || state == idStr(WP_LOWERING) || state == idStr(WP_HOLSTERED));
+bool idWeapon::ShowCrosshair() const
+{
+ // JDC: this code would never function as written, I'm assuming they wanted the following behavior
+ // return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
+ return !currentWeaponObject->IsRunning() || currentWeaponObject->IsStateRunning("Fire");
}
/*
@@ -1677,12 +1992,15 @@ bool idWeapon::ShowCrosshair(void) const {
idWeapon::CanDrop
=====================
*/
-bool idWeapon::CanDrop(void) const {
- if (!weaponDef || !worldModel.GetEntity()) {
+bool idWeapon::CanDrop() const
+{
+ if (!weaponDef || !worldModel.GetEntity())
+ {
return false;
}
const char* classname = weaponDef->dict.GetString("def_dropItem");
- if (!classname[0]) {
+ if (!classname[0])
+ {
return false;
}
return true;
@@ -1693,18 +2011,16 @@ bool idWeapon::CanDrop(void) const {
idWeapon::WeaponStolen
================
*/
-void idWeapon::WeaponStolen(void) {
+void idWeapon::WeaponStolen()
+{
assert(!gameLocal.isClient);
- if (projectileEnt) {
- if (isLinked) {
- SetState("WeaponStolen", 0);
- thread->Execute();
- }
+ if (projectileEnt)
+ {
projectileEnt = NULL;
}
// set to holstered so we can switch weapons right away
- status = WP_HOLSTERED;
+ //idealState = WP_HOLSTERED;
HideWeapon();
}
@@ -1714,15 +2030,19 @@ void idWeapon::WeaponStolen(void) {
idWeapon::DropItem
=====================
*/
-idEntity* idWeapon::DropItem(const idVec3& velocity, int activateDelay, int removeDelay, bool died) {
- if (!weaponDef || !worldModel.GetEntity()) {
+idEntity* idWeapon::DropItem(const idVec3& velocity, int activateDelay, int removeDelay, bool died)
+{
+ if (!weaponDef || !worldModel.GetEntity())
+ {
return NULL;
}
- if (!allowDrop) {
+ if (!allowDrop)
+ {
return NULL;
}
const char* classname = weaponDef->dict.GetString("def_dropItem");
- if (!classname[0]) {
+ if (!classname[0])
+ {
return NULL;
}
StopSound(SND_CHANNEL_BODY, true);
@@ -1737,36 +2057,6 @@ idEntity* idWeapon::DropItem(const idVec3& velocity, int activateDelay, int remo
***********************************************************************/
-/*
-=====================
-idWeapon::SetState
-=====================
-*/
-void idWeapon::SetState(const char* statename, int blendFrames) {
- const function_t* func;
-
- if (!isLinked) {
- return;
- }
-
- func = scriptObject.GetFunction(statename);
- if (!func) {
- assert(0);
- gameLocal.Error("Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName());
- }
-
- thread->CallFunction(this, func, true);
- state = statename;
-
- animBlendFrames = blendFrames;
- if (g_debugWeapon.GetBool()) {
- gameLocal.Printf("%d: weapon state : %s\n", gameLocal.time, statename);
- }
-
- idealState = "";
-}
-
-
/***********************************************************************
Particles/Effects
@@ -1778,8 +2068,10 @@ void idWeapon::SetState(const char* statename, int blendFrames) {
idWeapon::UpdateNozzelFx
================
*/
-void idWeapon::UpdateNozzleFx(void) {
- if (!nozzleFx) {
+void idWeapon::UpdateNozzleFx()
+{
+ if (!nozzleFx)
+ {
return;
}
@@ -1789,23 +2081,27 @@ void idWeapon::UpdateNozzleFx(void) {
int la = gameLocal.time - lastAttack + 1;
float s = 1.0f;
float l = 0.0f;
- if (la < nozzleFxFade) {
+ if (la < nozzleFxFade)
+ {
s = ((float)la / nozzleFxFade);
l = 1.0f - s;
}
renderEntity.shaderParms[5] = s;
renderEntity.shaderParms[6] = l;
- if (ventLightJointView == INVALID_JOINT) {
+ if (ventLightJointView == INVALID_JOINT)
+ {
return;
}
//
// vent light
//
- if (nozzleGlowHandle == -1) {
+ if (nozzleGlowHandle == -1)
+ {
memset(&nozzleGlow, 0, sizeof(nozzleGlow));
- if (owner) {
+ if (owner)
+ {
nozzleGlow.allowLightInViewID = owner->entityNumber + 1;
}
nozzleGlow.pointLight = true;
@@ -1834,22 +2130,26 @@ void idWeapon::UpdateNozzleFx(void) {
idWeapon::BloodSplat
================
*/
-bool idWeapon::BloodSplat(float size) {
+bool idWeapon::BloodSplat(float size)
+{
float s, c;
idMat3 localAxis, axistemp;
idVec3 localOrigin, normal;
- if (hasBloodSplat) {
+ if (hasBloodSplat)
+ {
return true;
}
hasBloodSplat = true;
- if (modelDefHandle < 0) {
+ if (modelDefHandle < 0)
+ {
return false;
}
- if (!GetGlobalJointTransform(true, ejectJointView, localOrigin, localAxis)) {
+ if (!GetGlobalJointTransform(true, ejectJointView, localOrigin, localAxis))
+ {
return false;
}
@@ -1899,22 +2199,26 @@ idWeapon::MuzzleRise
The machinegun and chaingun will incrementally back up as they are being fired
================
*/
-void idWeapon::MuzzleRise(idVec3& origin, idMat3& axis) {
+void idWeapon::MuzzleRise(idVec3& origin, idMat3& axis)
+{
int time;
float amount;
idAngles ang;
idVec3 offset;
time = kick_endtime - gameLocal.time;
- if (time <= 0) {
+ if (time <= 0)
+ {
return;
}
- if (muzzle_kick_maxtime <= 0) {
+ if (muzzle_kick_maxtime <= 0)
+ {
return;
}
- if (time > muzzle_kick_maxtime) {
+ if (time > muzzle_kick_maxtime)
+ {
time = muzzle_kick_maxtime;
}
@@ -1926,124 +2230,33 @@ void idWeapon::MuzzleRise(idVec3& origin, idMat3& axis) {
axis = ang.ToMat3() * axis;
}
-/*
-================
-idWeapon::ConstructScriptObject
-
-Called during idEntity::Spawn. Calls the constructor on the script object.
-Can be overridden by subclasses when a thread doesn't need to be allocated.
-================
-*/
-idThread* idWeapon::ConstructScriptObject(void) {
- const function_t* constructor;
-
- thread->EndThread();
-
- // call script object's constructor
- constructor = scriptObject.GetConstructor();
- if (!constructor) {
- gameLocal.Error("Missing constructor on '%s' for weapon", scriptObject.GetTypeName());
- }
-
- // init the script object's data
- scriptObject.ClearObject();
- thread->CallFunction(this, constructor, true);
- thread->Execute();
-
- return thread;
-}
-
-/*
-================
-idWeapon::DeconstructScriptObject
-
-Called during idEntity::~idEntity. Calls the destructor on the script object.
-Can be overridden by subclasses when a thread doesn't need to be allocated.
-Not called during idGameLocal::MapShutdown.
-================
-*/
-void idWeapon::DeconstructScriptObject(void) {
- const function_t* destructor;
-
- if (!thread) {
- return;
- }
-
- // don't bother calling the script object's destructor on map shutdown
- if (gameLocal.GameState() == GAMESTATE_SHUTDOWN) {
- return;
- }
-
- thread->EndThread();
-
- // call script object's destructor
- destructor = scriptObject.GetDestructor();
- if (destructor) {
- // start a thread that will run immediately and end
- thread->CallFunction(this, destructor, true);
- thread->Execute();
- thread->EndThread();
- }
-
- // clear out the object's memory
- scriptObject.ClearObject();
-}
-
-/*
-================
-idWeapon::UpdateScript
-================
-*/
-void idWeapon::UpdateScript(void) {
- int count;
-
- if (!isLinked) {
- return;
- }
-
- // only update the script on new frames
- if (!gameLocal.isNewFrame) {
- return;
- }
-
- if (idealState.Length()) {
- SetState(idealState, animBlendFrames);
- }
-
- // update script state, which may call Event_LaunchProjectiles, among other things
- count = 10;
- while ((thread->Execute() || idealState.Length()) && count--) {
- // happens for weapons with no clip (like grenades)
- if (idealState.Length()) {
- SetState(idealState, animBlendFrames);
- }
- }
-
- WEAPON_RELOAD = false;
-}
-
/*
================
idWeapon::AlertMonsters
================
*/
-void idWeapon::AlertMonsters(void) {
+void idWeapon::AlertMonsters()
+{
trace_t tr;
idEntity* ent;
idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
gameLocal.clip.TracePoint(tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner);
- if (g_debugWeapon.GetBool()) {
+ if (g_debugWeapon.GetBool())
+ {
gameRenderWorld->DebugLine(colorYellow, muzzleFlash.origin, end, 0);
gameRenderWorld->DebugArrow(colorGreen, muzzleFlash.origin, tr.endpos, 2, 0);
}
- if (tr.fraction < 1.0f) {
+ if (tr.fraction < 1.0f)
+ {
ent = gameLocal.GetTraceEntity(tr);
- if (ent->IsType(idAI::Type)) {
+ if (ent->IsType(idAI::Type))
+ {
static_cast(ent)->TouchedByFlashlight(owner);
}
- else if (ent->IsType(idTrigger::Type)) {
+ else if (ent->IsType(idTrigger::Type))
+ {
ent->Signal(SIG_TOUCH);
ent->ProcessEvent(&EV_Touch, owner, &tr);
}
@@ -2053,58 +2266,192 @@ void idWeapon::AlertMonsters(void) {
end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16(MS2SEC(gameLocal.time) * 31.34f);
end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16(MS2SEC(gameLocal.time) * 12.17f);
gameLocal.clip.TracePoint(tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner);
- if (g_debugWeapon.GetBool()) {
+ if (g_debugWeapon.GetBool())
+ {
gameRenderWorld->DebugLine(colorYellow, muzzleFlash.origin, end, 0);
gameRenderWorld->DebugArrow(colorGreen, muzzleFlash.origin, tr.endpos, 2, 0);
}
- if (tr.fraction < 1.0f) {
+ if (tr.fraction < 1.0f)
+ {
ent = gameLocal.GetTraceEntity(tr);
- if (ent->IsType(idAI::Type)) {
+ if (ent->IsType(idAI::Type))
+ {
static_cast(ent)->TouchedByFlashlight(owner);
}
- else if (ent->IsType(idTrigger::Type)) {
+ else if (ent->IsType(idTrigger::Type))
+ {
ent->Signal(SIG_TOUCH);
ent->ProcessEvent(&EV_Touch, owner, &tr);
}
}
}
+/*
+================
+idWeapon::GetMuzzlePositionWithHacks
+
+Some weapons that have a barrel joint either have it pointing in the wrong
+direction (rocket launcher), or don't animate it properly (pistol).
+
+For good 3D TV / head mounted display work, we need to display a laser sight
+in the world.
+
+Fixing the animated meshes would be ideal, but hacking it in code is
+the pragmatic move right now.
+
+Returns false for hands, grenades, and chainsaw.
+================
+*/
+bool idWeapon::GetMuzzlePositionWithHacks(idVec3& origin, idMat3& axis)
+{
+ // I couldn't find a simple enum to identify the weapons that need
+ // workaround hacks...
+ const idStr& weaponIconName = pdaIcon;
+
+ origin = playerViewOrigin;
+ axis = playerViewAxis;
+
+ if (weaponIconName == "guis/assets/hud/icons/grenade_new.tga")
+ {
+ return false;
+ }
+
+ if (weaponIconName == "guis/assets/hud/icons/chainsaw_new.tga")
+ {
+ return false;
+ }
+
+ if (weaponIconName == "guis/assets/hud/icons/soul_cube.tga")
+ {
+ return false;
+ }
+
+ if (barrelJointView != INVALID_JOINT)
+ {
+ GetGlobalJointTransform(true, barrelJointView, origin, axis);
+ }
+ else if (guiLightJointView != INVALID_JOINT)
+ {
+ GetGlobalJointTransform(true, guiLightJointView, origin, axis);
+ }
+ else
+ {
+ return false;
+ }
+
+ // get better axis joints for weapons where the barrelJointView isn't
+ // animated properly
+ idVec3 discardedOrigin;
+ if (weaponIconName == "guis/assets/hud/icons/pistol_new.tga")
+ {
+ // muzzle doesn't animate during firing, Bod does
+ const jointHandle_t bodJoint = animator.GetJointHandle("Bod");
+ GetGlobalJointTransform(true, bodJoint, discardedOrigin, axis);
+ }
+ if (weaponIconName == "guis/assets/hud/icons/rocketlauncher_new.tga")
+ {
+ // joint doesn't point straight, so rotate it
+ //std::swap( axis[0], axis[2] );
+ }
+ if (weaponIconName == "guis/assets/hud/icons/shotgun_new.tga")
+ {
+ // joint doesn't point straight, so rotate it
+ const jointHandle_t bodJoint = animator.GetJointHandle("trigger");
+ GetGlobalJointTransform(true, bodJoint, discardedOrigin, axis);
+ //std::swap( axis[0], axis[2] );
+ axis[0] = -axis[0];
+ }
+
+ // we probably should fix the above hacks above that are based on texture names above at some
+ // point
+ if (weaponDef != NULL)
+ {
+ if ((idStr::Icmp("weapon_shotgun_double", weaponDef->GetName()) == 0) || (idStr::Icmp("weapon_shotgun_double_mp", weaponDef->GetName()) == 0))
+ {
+ // joint doesn't point straight, so rotate it
+ //std::swap( axis[0], axis[2] );
+ }
+ else if (idStr::Icmp("weapon_grabber", weaponDef->GetName()) == 0)
+ {
+ idVec3 forward = axis[0];
+ forward.Normalize();
+ const float scaleOffset = 4.0f;
+ forward *= scaleOffset;
+ origin += forward;
+ }
+ }
+
+ return true;
+}
+
/*
================
idWeapon::PresentWeapon
================
*/
-void idWeapon::PresentWeapon(bool showViewModel) {
+void idWeapon::PresentWeapon(bool showViewModel)
+{
playerViewOrigin = owner->firstPersonViewOrigin;
playerViewAxis = owner->firstPersonViewAxis;
- // calculate weapon position based on player movement bobbing
- owner->CalculateViewWeaponPos(viewWeaponOrigin, viewWeaponAxis);
+ if (isPlayerFlashlight)
+ {
+ viewWeaponOrigin = playerViewOrigin;
+ viewWeaponAxis = playerViewAxis;
- // hide offset is for dropping the gun when approaching a GUI or NPC
- // This is simpler to manage than doing the weapon put-away animation
- if (gameLocal.time - hideStartTime < hideTime) {
- float frac = (float)(gameLocal.time - hideStartTime) / (float)hideTime;
- if (hideStart < hideEnd) {
- frac = 1.0f - frac;
- frac = 1.0f - frac * frac;
- }
- else {
- frac = frac * frac;
- }
- hideOffset = hideStart + (hideEnd - hideStart) * frac;
- }
- else {
- hideOffset = hideEnd;
- if (hide && disabled) {
- Hide();
- }
- }
- viewWeaponOrigin += hideOffset * viewWeaponAxis[2];
+ fraccos = cos((gameLocal.framenum & 255) / 127.0f * idMath::PI);
- // kick up based on repeat firing
- MuzzleRise(viewWeaponOrigin, viewWeaponAxis);
+ static unsigned int divisor = 32;
+ unsigned int val = (gameLocal.framenum + gameLocal.framenum / divisor) & 255;
+ fraccos2 = cos(val / 127.0f * idMath::PI);
+
+ static idVec3 baseAdjustPos = idVec3(-8.0f, -20.0f, -10.0f); // rt, fwd, up
+ static float pscale = 0.5f;
+ static float yscale = 0.125f;
+ idVec3 adjustPos = baseAdjustPos;// + ( idVec3( fraccos, 0.0f, fraccos2 ) * scale );
+ viewWeaponOrigin += adjustPos.x * viewWeaponAxis[1] + adjustPos.y * viewWeaponAxis[0] + adjustPos.z * viewWeaponAxis[2];
+ // viewWeaponOrigin += owner->viewBob;
+
+ static idAngles baseAdjustAng = idAngles(88.0f, 10.0f, 0.0f); //
+ idAngles adjustAng = baseAdjustAng + idAngles(fraccos * pscale, fraccos2 * yscale, 0.0f);
+ // adjustAng += owner->GetViewBobAngles();
+ viewWeaponAxis = adjustAng.ToMat3() * viewWeaponAxis;
+ }
+ else
+ {
+ // calculate weapon position based on player movement bobbing
+ owner->CalculateViewWeaponPos(viewWeaponOrigin, viewWeaponAxis);
+
+ // hide offset is for dropping the gun when approaching a GUI or NPC
+ // This is simpler to manage than doing the weapon put-away animation
+ if (gameLocal.time - hideStartTime < hideTime)
+ {
+ float frac = (float)(gameLocal.time - hideStartTime) / (float)hideTime;
+ if (hideStart < hideEnd)
+ {
+ frac = 1.0f - frac;
+ frac = 1.0f - frac * frac;
+ }
+ else
+ {
+ frac = frac * frac;
+ }
+ hideOffset = hideStart + (hideEnd - hideStart) * frac;
+ }
+ else
+ {
+ hideOffset = hideEnd;
+ if (hide && disabled)
+ {
+ Hide();
+ }
+ }
+ viewWeaponOrigin += hideOffset * viewWeaponAxis[2];
+
+ // kick up based on repeat firing
+ MuzzleRise(viewWeaponOrigin, viewWeaponAxis);
+ }
// set the physics position and orientation
GetPhysics()->SetOrigin(viewWeaponOrigin);
@@ -2112,7 +2459,7 @@ void idWeapon::PresentWeapon(bool showViewModel) {
UpdateVisuals();
// update the weapon script
- UpdateScript();
+ //UpdateScript();
UpdateGUI();
@@ -2123,131 +2470,218 @@ void idWeapon::PresentWeapon(bool showViewModel) {
renderEntity.allowSurfaceInViewID = owner->entityNumber + 1;
// crunch the depth range so it never pokes into walls this breaks the machine gun gui
- renderEntity.weaponDepthHack = true;
+ renderEntity.weaponDepthHack = g_useWeaponDepthHack.GetBool();
// present the model
- if (showViewModel) {
+ if (showViewModel)
+ {
Present();
}
- else {
+ else
+ {
FreeModelDef();
}
- if (worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity()) {
+ if (worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity())
+ {
// deal with the third-person visible world model
// don't show shadows of the world model in first person
- if (gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool()) {
+ if (gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool())
+ {
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0;
}
- else {
+ else
+ {
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber + 1;
worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
}
}
- if (nozzleFx) {
+ if (nozzleFx)
+ {
UpdateNozzleFx();
}
// muzzle smoke
- if (showViewModel && !disabled && weaponSmoke && (weaponSmokeStartTime != 0)) {
+ if (showViewModel && !disabled && weaponSmoke && (weaponSmokeStartTime != 0))
+ {
// use the barrel joint if available
- if (barrelJointView) {
+
+ if (smokeJointView != INVALID_JOINT)
+ {
+ GetGlobalJointTransform(true, smokeJointView, muzzleOrigin, muzzleAxis);
+ }
+ else if (barrelJointView != INVALID_JOINT)
+ {
GetGlobalJointTransform(true, barrelJointView, muzzleOrigin, muzzleAxis);
}
- else {
+ else
+ {
// default to going straight out the view
muzzleOrigin = playerViewOrigin;
muzzleAxis = playerViewAxis;
}
// spit out a particle
- if (!gameLocal.smokeParticles->EmitSmoke(weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis)) {
+ if (!gameLocal.smokeParticles->EmitSmoke(weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis))
+ {
weaponSmokeStartTime = (continuousSmoke) ? gameLocal.time : 0;
}
}
- if (showViewModel && strikeSmoke && strikeSmokeStartTime != 0) {
+ if (showViewModel && strikeSmoke && strikeSmokeStartTime != 0)
+ {
// spit out a particle
- if (!gameLocal.smokeParticles->EmitSmoke(strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis)) {
+ if (!gameLocal.smokeParticles->EmitSmoke(strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis))
+ {
strikeSmokeStartTime = 0;
}
}
- // Update the separate weapon-fire muzzle light. This is the actual light
- // spawned by firing the gun, and it stays attached to the flash joint while
- // fading out independently from the flashlight/script light.
- if (IsHidden()) {
- FreeWeaponFireMuzzleLight(entityNumber);
- }
- else {
- idVec3 fireViewOrigin;
- idMat3 fireViewAxis;
- idVec3 fireWorldOrigin;
- idMat3 fireWorldAxis;
+ if (showViewModel && !hide)
+ {
- if (flashJointView != INVALID_JOINT && GetGlobalJointTransform(true, flashJointView, fireViewOrigin, fireViewAxis)) {
- idVec3 fireStart = fireViewOrigin - playerViewAxis[0] * 16.0f;
- idVec3 fireEnd = fireViewOrigin + playerViewAxis[0] * 8.0f;
- trace_t fireTr;
- gameLocal.clip.TracePoint(fireTr, fireStart, fireEnd, MASK_SHOT_RENDERMODEL, owner);
- fireViewOrigin = fireTr.endpos - playerViewAxis[0] * 8.0f;
+ for (int i = 0; i < weaponParticles.Num(); i++)
+ {
+ WeaponParticle_t* part = weaponParticles.GetIndex(i);
- if (flashJointWorld != INVALID_JOINT && GetGlobalJointTransform(false, flashJointWorld, fireWorldOrigin, fireWorldAxis)) {
- // valid world joint
+ if (part->active)
+ {
+ if (part->smoke)
+ {
+ if (part->joint != INVALID_JOINT)
+ {
+ GetGlobalJointTransform(true, part->joint, muzzleOrigin, muzzleAxis);
+ }
+ else
+ {
+ // default to going straight out the view
+ muzzleOrigin = playerViewOrigin;
+ muzzleAxis = playerViewAxis;
+ }
+ if (!gameLocal.smokeParticles->EmitSmoke(part->particle, part->startTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis))
+ {
+ part->active = false; // all done
+ part->startTime = 0;
+ }
+ }
+ else
+ {
+ if (part->emitter != NULL)
+ {
+ //Manually update the position of the emitter so it follows the weapon
+ renderEntity_t* rendEnt = part->emitter->GetRenderEntity();
+ GetGlobalJointTransform(true, part->joint, rendEnt->origin, rendEnt->axis);
+
+ if (part->emitter->GetModelDefHandle() != -1)
+ {
+ gameRenderWorld->UpdateEntityDef(part->emitter->GetModelDefHandle(), rendEnt);
+ }
+ }
+ }
}
- else {
- fireWorldOrigin = fireViewOrigin;
- fireWorldAxis = fireViewAxis;
- }
-
- UpdateWeaponFireMuzzleLight(entityNumber, fireViewOrigin, fireViewAxis, fireWorldOrigin, fireWorldAxis);
}
- else {
- FreeWeaponFireMuzzleLight(entityNumber);
+
+ for (int i = 0; i < weaponLights.Num(); i++)
+ {
+ WeaponLight_t* light = weaponLights.GetIndex(i);
+
+ if (light->active)
+ {
+
+ GetGlobalJointTransform(true, light->joint, light->light.origin, light->light.axis);
+ if ((light->lightHandle != -1))
+ {
+ gameRenderWorld->UpdateLightDef(light->lightHandle, &light->light);
+ }
+ else
+ {
+ light->lightHandle = gameRenderWorld->AddLightDef(&light->light);
+ }
+ }
}
}
- // remove the old script/flashlight muzzle light when it's done
- if ((!lightOn && (gameLocal.time >= muzzleFlashEnd)) || IsHidden()) {
- if (muzzleFlashHandle != -1) {
+ // remove the muzzle flash light when it's done
+ if ((!lightOn && (gameLocal.time >= muzzleFlashEnd)) || IsHidden())
+ {
+ if (muzzleFlashHandle != -1)
+ {
gameRenderWorld->FreeLightDef(muzzleFlashHandle);
muzzleFlashHandle = -1;
}
- if (worldMuzzleFlashHandle != -1) {
+ if (worldMuzzleFlashHandle != -1)
+ {
gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
worldMuzzleFlashHandle = -1;
}
}
// update the muzzle flash light, so it moves with the gun
- if (muzzleFlashHandle != -1) {
+ if (muzzleFlashHandle != -1)
+ {
UpdateFlashPosition();
gameRenderWorld->UpdateLightDef(muzzleFlashHandle, &muzzleFlash);
gameRenderWorld->UpdateLightDef(worldMuzzleFlashHandle, &worldMuzzleFlash);
// wake up monsters with the flashlight
- if (!gameLocal.isMultiplayer && lightOn && !owner->fl.notarget) {
+ if (!gameLocal.isMultiplayer && lightOn && !owner->fl.notarget)
+ {
AlertMonsters();
}
}
// update the gui light
- if (guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT) {
+ if (guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT)
+ {
GetGlobalJointTransform(true, guiLightJointView, guiLight.origin, guiLight.axis);
- if ((guiLightHandle != -1)) {
+ if ((guiLightHandle != -1))
+ {
gameRenderWorld->UpdateLightDef(guiLightHandle, &guiLight);
}
- else {
+ else
+ {
guiLightHandle = gameRenderWorld->AddLightDef(&guiLight);
}
}
-
- if (status != WP_READY && sndHum) {
- StopSound(SND_CHANNEL_BODY, false);
- }
+ // jmarshall - eval
+ //if( status != WP_READY && sndHum )
+ //{
+ // StopSound( SND_CHANNEL_BODY, false );
+ //}
+ // jmarshall end
UpdateSound();
+
+ // constant rumble...
+ float highMagnitude = weaponDef->dict.GetFloat("controllerConstantShakeHighMag");
+ int highDuration = weaponDef->dict.GetInt("controllerConstantShakeHighTime");
+ float lowMagnitude = weaponDef->dict.GetFloat("controllerConstantShakeLowMag");
+ int lowDuration = weaponDef->dict.GetInt("controllerConstantShakeLowTime");
+
+ //if( owner->IsLocallyControlled() )
+ //{
+ // owner->SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
+ //}
+}
+
+/*
+================
+idWeapon::RemoveMuzzleFlashlight
+================
+*/
+void idWeapon::RemoveMuzzleFlashlight()
+{
+ if (muzzleFlashHandle != -1)
+ {
+ gameRenderWorld->FreeLightDef(muzzleFlashHandle);
+ muzzleFlashHandle = -1;
+ }
+ if (worldMuzzleFlashHandle != -1)
+ {
+ gameRenderWorld->FreeLightDef(worldMuzzleFlashHandle);
+ worldMuzzleFlashHandle = -1;
+ }
}
/*
@@ -2255,22 +2689,10 @@ void idWeapon::PresentWeapon(bool showViewModel) {
idWeapon::EnterCinematic
================
*/
-void idWeapon::EnterCinematic(void) {
+void idWeapon::EnterCinematic()
+{
StopSound(SND_CHANNEL_ANY, false);
- if (isLinked) {
- SetState("EnterCinematic", 0);
- thread->Execute();
-
- WEAPON_ATTACK = false;
- WEAPON_RELOAD = false;
- WEAPON_NETRELOAD = false;
- WEAPON_NETENDRELOAD = false;
- WEAPON_NETFIRING = false;
- WEAPON_RAISEWEAPON = false;
- WEAPON_LOWERWEAPON = false;
- }
-
disabled = true;
LowerWeapon();
@@ -2281,14 +2703,10 @@ void idWeapon::EnterCinematic(void) {
idWeapon::ExitCinematic
================
*/
-void idWeapon::ExitCinematic(void) {
+void idWeapon::ExitCinematic()
+{
disabled = false;
- if (isLinked) {
- SetState("ExitCinematic", 0);
- thread->Execute();
- }
-
RaiseWeapon();
}
@@ -2297,11 +2715,9 @@ void idWeapon::ExitCinematic(void) {
idWeapon::NetCatchup
================
*/
-void idWeapon::NetCatchup(void) {
- if (isLinked) {
- SetState("NetCatchup", 0);
- thread->Execute();
- }
+void idWeapon::NetCatchup()
+{
+
}
/*
@@ -2309,7 +2725,8 @@ void idWeapon::NetCatchup(void) {
idWeapon::GetZoomFov
================
*/
-int idWeapon::GetZoomFov(void) {
+int idWeapon::GetZoomFov()
+{
return zoomFov;
}
@@ -2318,7 +2735,8 @@ int idWeapon::GetZoomFov(void) {
idWeapon::GetWeaponAngleOffsets
================
*/
-void idWeapon::GetWeaponAngleOffsets(int* average, float* scale, float* max) {
+void idWeapon::GetWeaponAngleOffsets(int* average, float* scale, float* max)
+{
*average = weaponAngleOffsetAverages;
*scale = weaponAngleOffsetScale;
*max = weaponAngleOffsetMax;
@@ -2329,7 +2747,8 @@ void idWeapon::GetWeaponAngleOffsets(int* average, float* scale, float* max) {
idWeapon::GetWeaponTimeOffsets
================
*/
-void idWeapon::GetWeaponTimeOffsets(float* time, float* scale) {
+void idWeapon::GetWeaponTimeOffsets(float* time, float* scale)
+{
*time = weaponOffsetTime;
*scale = weaponOffsetScale;
}
@@ -2346,27 +2765,34 @@ void idWeapon::GetWeaponTimeOffsets(float* time, float* scale) {
idWeapon::GetAmmoNumForName
================
*/
-ammo_t idWeapon::GetAmmoNumForName(const char* ammoname) {
+ammo_t idWeapon::GetAmmoNumForName(const char* ammoname)
+{
int num;
const idDict* ammoDict;
assert(ammoname);
ammoDict = gameLocal.FindEntityDefDict("ammo_types", false);
- if (!ammoDict) {
+ if (ammoDict == NULL)
+ {
gameLocal.Error("Could not find entity definition for 'ammo_types'\n");
- }
-
- if (!ammoname[0]) {
return 0;
}
- if (!ammoDict->GetInt(ammoname, "-1", num)) {
- gameLocal.Error("Unknown ammo type '%s'", ammoname);
+ if (!ammoname[0])
+ {
+ return 0;
}
- if ((num < 0) || (num >= AMMO_NUMTYPES)) {
- gameLocal.Error("Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES);
+ if (!ammoDict->GetInt(ammoname, "-1", num))
+ {
+
+ }
+
+ if ((num < 0) || (num >= AMMO_NUMTYPES))
+ {
+ //gameLocal.Warning( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES );
+ num = 0;
}
return (ammo_t)num;
@@ -2377,7 +2803,8 @@ ammo_t idWeapon::GetAmmoNumForName(const char* ammoname) {
idWeapon::GetAmmoNameForNum
================
*/
-const char* idWeapon::GetAmmoNameForNum(ammo_t ammonum) {
+const char* idWeapon::GetAmmoNameForNum(ammo_t ammonum)
+{
int i;
int num;
const idDict* ammoDict;
@@ -2385,16 +2812,20 @@ const char* idWeapon::GetAmmoNameForNum(ammo_t ammonum) {
char text[32];
ammoDict = gameLocal.FindEntityDefDict("ammo_types", false);
- if (!ammoDict) {
+ if (ammoDict == NULL)
+ {
gameLocal.Error("Could not find entity definition for 'ammo_types'\n");
+ return NULL;
}
sprintf(text, "%d", ammonum);
num = ammoDict->GetNumKeyVals();
- for (i = 0; i < num; i++) {
+ for (i = 0; i < num; i++)
+ {
kv = ammoDict->GetKeyVal(i);
- if (kv->GetValue() == text) {
+ if (kv->GetValue() == text)
+ {
return kv->GetKey();
}
}
@@ -2407,24 +2838,29 @@ const char* idWeapon::GetAmmoNameForNum(ammo_t ammonum) {
idWeapon::GetAmmoPickupNameForNum
================
*/
-const char* idWeapon::GetAmmoPickupNameForNum(ammo_t ammonum) {
+const char* idWeapon::GetAmmoPickupNameForNum(ammo_t ammonum)
+{
int i;
int num;
const idDict* ammoDict;
const idKeyValue* kv;
ammoDict = gameLocal.FindEntityDefDict("ammo_names", false);
- if (!ammoDict) {
+ if (!ammoDict)
+ {
gameLocal.Error("Could not find entity definition for 'ammo_names'\n");
}
const char* name = GetAmmoNameForNum(ammonum);
- if (name && *name) {
+ if (name != NULL && *name != NULL)
+ {
num = ammoDict->GetNumKeyVals();
- for (i = 0; i < num; i++) {
+ for (i = 0; i < num; i++)
+ {
kv = ammoDict->GetKeyVal(i);
- if (idStr::Icmp(kv->GetKey(), name) == 0) {
+ if (idStr::Icmp(kv->GetKey(), name) == 0)
+ {
return kv->GetValue();
}
}
@@ -2438,12 +2874,22 @@ const char* idWeapon::GetAmmoPickupNameForNum(ammo_t ammonum) {
idWeapon::AmmoAvailable
================
*/
-int idWeapon::AmmoAvailable(void) const {
- if (owner) {
+int idWeapon::AmmoAvailable() const
+{
+ if (owner)
+ {
return owner->inventory.HasAmmo(ammoType, ammoRequired);
}
- else {
- return 0;
+ else
+ {
+ //if( g_infiniteAmmo.GetBool() )
+ //{
+ // return 10; // arbitrary number, just so whatever's calling thinks there's sufficient ammo...
+ //}
+ //else
+ {
+ return 0;
+ }
}
}
@@ -2452,8 +2898,9 @@ int idWeapon::AmmoAvailable(void) const {
idWeapon::AmmoInClip
================
*/
-int idWeapon::AmmoInClip(void) const {
- return ammoClip;
+int idWeapon::AmmoInClip() const
+{
+ return ammoClip.Get();
}
/*
@@ -2461,7 +2908,8 @@ int idWeapon::AmmoInClip(void) const {
idWeapon::ResetAmmoClip
================
*/
-void idWeapon::ResetAmmoClip(void) {
+void idWeapon::ResetAmmoClip()
+{
ammoClip = -1;
}
@@ -2470,7 +2918,8 @@ void idWeapon::ResetAmmoClip(void) {
idWeapon::GetAmmoType
================
*/
-ammo_t idWeapon::GetAmmoType(void) const {
+ammo_t idWeapon::GetAmmoType() const
+{
return ammoType;
}
@@ -2479,7 +2928,8 @@ ammo_t idWeapon::GetAmmoType(void) const {
idWeapon::ClipSize
================
*/
-int idWeapon::ClipSize(void) const {
+int idWeapon::ClipSize() const
+{
return clipSize;
}
@@ -2488,7 +2938,8 @@ int idWeapon::ClipSize(void) const {
idWeapon::LowAmmo
================
*/
-int idWeapon::LowAmmo() const {
+int idWeapon::LowAmmo() const
+{
return lowAmmo;
}
@@ -2497,17 +2948,52 @@ int idWeapon::LowAmmo() const {
idWeapon::AmmoRequired
================
*/
-int idWeapon::AmmoRequired(void) const {
+int idWeapon::AmmoRequired() const
+{
return ammoRequired;
}
+/*
+================
+idWeapon::GetGrabberState
+
+Returns the current grabberState
+================
+*/
+int idWeapon::GetGrabberState() const
+{
+
+ return grabberState;
+}
+
+/*
+================
+idWeapon::AmmoCount
+
+Returns the total number of rounds regardless of the required ammo
+================
+*/
+int idWeapon::AmmoCount() const
+{
+
+ if (owner)
+ {
+ return owner->inventory.HasAmmo(ammoType, 1);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
/*
================
idWeapon::WriteToSnapshot
================
*/
-void idWeapon::WriteToSnapshot(idBitMsgDelta& msg) const {
- msg.WriteBits(ammoClip, ASYNC_PLAYER_INV_CLIP_BITS);
+void idWeapon::WriteToSnapshot(idBitMsg& msg) const
+{
+ msg.WriteBits(ammoClip.Get(), ASYNC_PLAYER_INV_CLIP_BITS);
msg.WriteBits(worldModel.GetSpawnId(), 32);
msg.WriteBits(lightOn, 1);
msg.WriteBits(isFiring ? 1 : 0, 1);
@@ -2518,31 +3004,50 @@ void idWeapon::WriteToSnapshot(idBitMsgDelta& msg) const {
idWeapon::ReadFromSnapshot
================
*/
-void idWeapon::ReadFromSnapshot(const idBitMsgDelta& msg) {
- ammoClip = msg.ReadBits(ASYNC_PLAYER_INV_CLIP_BITS);
+void idWeapon::ReadFromSnapshot(const idBitMsg& msg)
+{
+ const int snapshotAmmoClip = msg.ReadBits(ASYNC_PLAYER_INV_CLIP_BITS);
worldModel.SetSpawnId(msg.ReadBits(32));
- bool snapLight = msg.ReadBits(1) != 0;
+ const bool snapshotLightOn = msg.ReadBits(1) != 0;
isFiring = msg.ReadBits(1) != 0;
+ // Local clients predict the ammo in the clip. Only use the ammo cpunt from the snapshot for local clients
+ // if the server has processed the same usercmd in which we predicted the ammo count.
+ if (owner != NULL)
+ {
+ ammoClip.UpdateFromSnapshot(snapshotAmmoClip, owner->entityNumber);
+ }
+
// WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client
- if (owner && gameLocal.localClientNum != owner->entityNumber && WEAPON_NETFIRING.IsLinked()) {
+ //if ( owner && !owner->IsLocallyControlled() && WEAPON_NETFIRING.IsLinked() ) {
+ //
+ // // immediately go to the firing state so we don't skip fire animations
+ // if ( !WEAPON_NETFIRING && isFiring ) {
+ // idealState = "Fire";
+ // }
+ //
+ // // immediately switch back to idle
+ // if ( WEAPON_NETFIRING && !isFiring ) {
+ // idealState = "Idle";
+ // }
+ //
+ // WEAPON_NETFIRING = isFiring;
+ // WEAPON_ATTACK = isFiring;
+ //}
- // immediately go to the firing state so we don't skip fire animations
- if (!WEAPON_NETFIRING && isFiring) {
- idealState = "Fire";
- }
-
- // immediately switch back to idle
- if (WEAPON_NETFIRING && !isFiring) {
- idealState = "Idle";
- }
-
- WEAPON_NETFIRING = isFiring;
- }
-
- if (snapLight != lightOn) {
- Reload();
- }
+ // Only update the flashlight state if it has changed, and if this isn't the local player.
+ // The local player sets their flashlight immediately for responsiveness.
+ //if( owner != NULL && !owner->IsLocallyControlled() && lightOn != snapshotLightOn )
+ //{
+ // if( snapshotLightOn )
+ // {
+ // FlashlightOn();
+ // }
+ // else
+ // {
+ // FlashlightOff();
+ // }
+ //}
}
/*
@@ -2550,38 +3055,46 @@ void idWeapon::ReadFromSnapshot(const idBitMsgDelta& msg) {
idWeapon::ClientReceiveEvent
================
*/
-bool idWeapon::ClientReceiveEvent(int event, int time, const idBitMsg& msg) {
+bool idWeapon::ClientReceiveEvent(int event, int time, const idBitMsg& msg)
+{
- switch (event) {
- case EVENT_RELOAD: {
- if (gameLocal.time - time < 1000) {
- if (WEAPON_NETRELOAD.IsLinked()) {
- WEAPON_NETRELOAD = true;
- WEAPON_NETENDRELOAD = false;
- }
- }
+ switch (event)
+ {
+ case EVENT_RELOAD:
+ {
+ // Local clients predict reloads, only process this event for remote clients.
+ //if ( owner != NULL && !owner->IsLocallyControlled() && ( gameLocal.time - time < 1000 ) ) {
+ // if ( WEAPON_NETRELOAD.IsLinked() ) {
+ // WEAPON_NETRELOAD = true;
+ // WEAPON_NETENDRELOAD = false;
+ // }
+ //}
return true;
}
- case EVENT_ENDRELOAD: {
- if (WEAPON_NETENDRELOAD.IsLinked()) {
- WEAPON_NETENDRELOAD = true;
- }
+ case EVENT_ENDRELOAD:
+ {
+ // Local clients predict reloads, only process this event for remote clients.
+ //if ( owner != NULL && !owner->IsLocallyControlled() && WEAPON_NETENDRELOAD.IsLinked() ) {
+ // WEAPON_NETENDRELOAD = true;
+ //}
return true;
}
- case EVENT_CHANGESKIN: {
+ case EVENT_CHANGESKIN:
+ {
int index = gameLocal.ClientRemapDecl(DECL_SKIN, msg.ReadLong());
renderEntity.customSkin = (index != -1) ? static_cast(declManager->DeclByIndex(DECL_SKIN, index)) : NULL;
UpdateVisuals();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->SetSkin(renderEntity.customSkin);
}
return true;
}
- default: {
+ default:
+ {
return idEntity::ClientReceiveEvent(event, time, msg);
}
}
- return false;
}
/***********************************************************************
@@ -2595,7 +3108,8 @@ bool idWeapon::ClientReceiveEvent(int event, int time, const idBitMsg& msg) {
idWeapon::Event_Clear
===============
*/
-void idWeapon::Event_Clear(void) {
+void idWeapon::Event_Clear()
+{
Clear();
}
@@ -2604,48 +3118,27 @@ void idWeapon::Event_Clear(void) {
idWeapon::Event_GetOwner
===============
*/
-void idWeapon::Event_GetOwner(void) {
+void idWeapon::Event_GetOwner()
+{
idThread::ReturnEntity(owner);
}
-/*
-===============
-idWeapon::Event_WeaponState
-===============
-*/
-void idWeapon::Event_WeaponState(const char* statename, int blendFrames) {
- const function_t* func;
-
- func = scriptObject.GetFunction(statename);
- if (!func) {
- assert(0);
- gameLocal.Error("Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName());
- }
-
- idealState = statename;
-
- if (!idealState.Icmp("Fire")) {
- isFiring = true;
- }
- else {
- isFiring = false;
- }
-
- animBlendFrames = blendFrames;
- thread->DoneProcessing();
-}
-
/*
===============
idWeapon::Event_WeaponReady
===============
*/
-void idWeapon::Event_WeaponReady(void) {
- status = WP_READY;
- if (isLinked) {
- WEAPON_RAISEWEAPON = false;
- }
- if (sndHum) {
+void idWeapon::Event_WeaponReady()
+{
+ //status = WP_READY;
+ currentWeaponObject->SetState("Rising");
+ currentWeaponObject->AppendState("Idle");
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_READY \n" );
+ //if ( isLinked ) {
+ // WEAPON_RAISEWEAPON = false;
+ //}
+ if (sndHum)
+ {
StartSoundShader(sndHum, SND_CHANNEL_BODY, 0, false, NULL);
}
@@ -2656,11 +3149,11 @@ void idWeapon::Event_WeaponReady(void) {
idWeapon::Event_WeaponOutOfAmmo
===============
*/
-void idWeapon::Event_WeaponOutOfAmmo(void) {
- status = WP_OUTOFAMMO;
- if (isLinked) {
- WEAPON_RAISEWEAPON = false;
- }
+void idWeapon::Event_WeaponOutOfAmmo()
+{
+ //status = WP_OUTOFAMMO;
+ OutOfAmmo = true;
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_OUTOFAMMO \n" );
}
/*
@@ -2668,8 +3161,10 @@ void idWeapon::Event_WeaponOutOfAmmo(void) {
idWeapon::Event_WeaponReloading
===============
*/
-void idWeapon::Event_WeaponReloading(void) {
- status = WP_RELOAD;
+void idWeapon::Event_WeaponReloading()
+{
+ Reload();
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_RELOAD \n" );
}
/*
@@ -2677,11 +3172,10 @@ void idWeapon::Event_WeaponReloading(void) {
idWeapon::Event_WeaponHolstered
===============
*/
-void idWeapon::Event_WeaponHolstered(void) {
- status = WP_HOLSTERED;
- if (isLinked) {
- WEAPON_LOWERWEAPON = false;
- }
+void idWeapon::Event_WeaponHolstered()
+{
+ //status = WP_HOLSTERED;
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_HOLSTERED \n" );
}
/*
@@ -2689,11 +3183,10 @@ void idWeapon::Event_WeaponHolstered(void) {
idWeapon::Event_WeaponRising
===============
*/
-void idWeapon::Event_WeaponRising(void) {
- status = WP_RISING;
- if (isLinked) {
- WEAPON_LOWERWEAPON = false;
- }
+void idWeapon::Event_WeaponRising()
+{
+ Raise();
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_RISING \n" );
owner->WeaponRisingCallback();
}
@@ -2702,11 +3195,10 @@ void idWeapon::Event_WeaponRising(void) {
idWeapon::Event_WeaponLowering
===============
*/
-void idWeapon::Event_WeaponLowering(void) {
- status = WP_LOWERING;
- if (isLinked) {
- WEAPON_RAISEWEAPON = false;
- }
+void idWeapon::Event_WeaponLowering()
+{
+ LowerWeapon();
+ // idLib::PrintfIf( g_debugWeapon.GetBool(), "Weapon Status WP_LOWERING \n" );
owner->WeaponLoweringCallback();
}
@@ -2715,15 +3207,19 @@ void idWeapon::Event_WeaponLowering(void) {
idWeapon::Event_UseAmmo
===============
*/
-void idWeapon::Event_UseAmmo(int amount) {
+void idWeapon::Event_UseAmmo(int amount)
+{
if (gameLocal.isClient) {
return;
}
+
owner->inventory.UseAmmo(ammoType, (powerAmmo) ? amount : (amount * ammoRequired));
- if (clipSize && ammoRequired) {
+ if (clipSize && ammoRequired)
+ {
ammoClip -= powerAmmo ? amount : (amount * ammoRequired);
- if (ammoClip < 0) {
+ if (ammoClip.Get() < 0)
+ {
ammoClip = 0;
}
}
@@ -2734,22 +3230,32 @@ void idWeapon::Event_UseAmmo(int amount) {
idWeapon::Event_AddToClip
===============
*/
-void idWeapon::Event_AddToClip(int amount) {
+void idWeapon::Event_AddToClip(int amount)
+{
int ammoAvail;
if (gameLocal.isClient) {
return;
}
+ int oldAmmo = ammoClip.Get();
+ ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired) + AmmoInClip();
+
ammoClip += amount;
- if (ammoClip > clipSize) {
+ if (ammoClip.Get() > clipSize)
+ {
ammoClip = clipSize;
}
- ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
- if (ammoClip > ammoAvail) {
+
+ if (ammoClip.Get() > ammoAvail)
+ {
ammoClip = ammoAvail;
}
+
+ // for shared ammo we need to use the ammo when it is moved into the clip
+ int usedAmmo = ammoClip.Get() - oldAmmo;
+ owner->inventory.UseAmmo(ammoType, usedAmmo);
}
/*
@@ -2757,19 +3263,33 @@ void idWeapon::Event_AddToClip(int amount) {
idWeapon::Event_AmmoInClip
===============
*/
-void idWeapon::Event_AmmoInClip(void) {
+void idWeapon::Event_AmmoInClip()
+{
int ammo = AmmoInClip();
idThread::ReturnFloat(ammo);
}
+/*
+===============
+idWeapon::AmmoAvailable
+===============
+*/
+int idWeapon::AmmoAvailable()
+{
+ int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
+ ammoAvail += AmmoInClip();
+
+ return ammoAvail;
+}
+
/*
===============
idWeapon::Event_AmmoAvailable
===============
*/
-void idWeapon::Event_AmmoAvailable(void) {
- int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
- idThread::ReturnFloat(ammoAvail);
+void idWeapon::Event_AmmoAvailable()
+{
+ idThread::ReturnFloat(AmmoAvailable());
}
/*
@@ -2777,7 +3297,8 @@ void idWeapon::Event_AmmoAvailable(void) {
idWeapon::Event_TotalAmmoCount
===============
*/
-void idWeapon::Event_TotalAmmoCount(void) {
+void idWeapon::Event_TotalAmmoCount()
+{
int ammoAvail = owner->inventory.HasAmmo(ammoType, 1);
idThread::ReturnFloat(ammoAvail);
}
@@ -2787,7 +3308,8 @@ void idWeapon::Event_TotalAmmoCount(void) {
idWeapon::Event_ClipSize
===============
*/
-void idWeapon::Event_ClipSize(void) {
+void idWeapon::Event_ClipSize()
+{
idThread::ReturnFloat(clipSize);
}
@@ -2796,7 +3318,8 @@ void idWeapon::Event_ClipSize(void) {
idWeapon::Event_AutoReload
===============
*/
-void idWeapon::Event_AutoReload(void) {
+void idWeapon::Event_AutoReload()
+{
assert(owner);
if (gameLocal.isClient) {
idThread::ReturnFloat(0.0f);
@@ -2810,10 +3333,12 @@ void idWeapon::Event_AutoReload(void) {
idWeapon::Event_NetReload
===============
*/
-void idWeapon::Event_NetReload(void) {
+void idWeapon::Event_NetReload()
+{
assert(owner);
- if (gameLocal.isServer) {
- ServerSendEvent(EVENT_RELOAD, NULL, false, -1);
+ if (gameLocal.isServer)
+ {
+ ServerSendEvent(EVENT_RELOAD, NULL, false, 0);
}
}
@@ -2822,10 +3347,12 @@ void idWeapon::Event_NetReload(void) {
idWeapon::Event_NetEndReload
===============
*/
-void idWeapon::Event_NetEndReload(void) {
+void idWeapon::Event_NetEndReload()
+{
assert(owner);
- if (gameLocal.isServer) {
- ServerSendEvent(EVENT_ENDRELOAD, NULL, false, -1);
+ if (gameLocal.isServer)
+ {
+ ServerSendEvent(EVENT_ENDRELOAD, NULL, false, 0);
}
}
@@ -2834,24 +3361,30 @@ void idWeapon::Event_NetEndReload(void) {
idWeapon::Event_PlayAnim
===============
*/
-void idWeapon::Event_PlayAnim(int channel, const char* animname) {
+void idWeapon::Event_PlayAnim(int channel, const char* animname, bool loop)
+{
int anim;
anim = animator.GetAnim(animname);
- if (!anim) {
+ if (!anim)
+ {
gameLocal.Warning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
animator.Clear(channel, gameLocal.time, FRAME2MS(animBlendFrames));
animDoneTime = 0;
}
- else {
- if (!(owner && owner->GetInfluenceLevel())) {
+ else
+ {
+ if (!(owner && owner->GetInfluenceLevel()))
+ {
Show();
}
animator.PlayAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
animDoneTime = animator.CurrentAnim(channel)->GetEndTime();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
anim = worldModel.GetEntity()->GetAnimator()->GetAnim(animname);
- if (anim) {
+ if (anim)
+ {
worldModel.GetEntity()->GetAnimator()->PlayAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
}
}
@@ -2865,22 +3398,27 @@ void idWeapon::Event_PlayAnim(int channel, const char* animname) {
idWeapon::Event_PlayCycle
===============
*/
-void idWeapon::Event_PlayCycle(int channel, const char* animname) {
+void idWeapon::Event_PlayCycle(int channel, const char* animname)
+{
int anim;
anim = animator.GetAnim(animname);
- if (!anim) {
+ if (!anim)
+ {
gameLocal.Warning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
animator.Clear(channel, gameLocal.time, FRAME2MS(animBlendFrames));
animDoneTime = 0;
}
- else {
- if (!(owner && owner->GetInfluenceLevel())) {
+ else
+ {
+ if (!(owner && owner->GetInfluenceLevel()))
+ {
Show();
}
animator.CycleAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
animDoneTime = animator.CurrentAnim(channel)->GetEndTime();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
anim = worldModel.GetEntity()->GetAnimator()->GetAnim(animname);
worldModel.GetEntity()->GetAnimator()->CycleAnim(channel, anim, gameLocal.time, FRAME2MS(animBlendFrames));
}
@@ -2894,13 +3432,13 @@ void idWeapon::Event_PlayCycle(int channel, const char* animname) {
idWeapon::Event_AnimDone
===============
*/
-void idWeapon::Event_AnimDone(int channel, int blendFrames) {
- if (animDoneTime - FRAME2MS(blendFrames) <= gameLocal.time) {
- idThread::ReturnInt(true);
- }
- else {
- idThread::ReturnInt(false);
+bool idWeapon::Event_AnimDone(int channel, int blendFrames)
+{
+ if (animDoneTime - FRAME2MS(blendFrames) <= gameLocal.time)
+ {
+ return true;
}
+ return false;
}
/*
@@ -2908,7 +3446,8 @@ void idWeapon::Event_AnimDone(int channel, int blendFrames) {
idWeapon::Event_SetBlendFrames
===============
*/
-void idWeapon::Event_SetBlendFrames(int channel, int blendFrames) {
+void idWeapon::Event_SetBlendFrames(int channel, int blendFrames)
+{
animBlendFrames = blendFrames;
}
@@ -2917,7 +3456,8 @@ void idWeapon::Event_SetBlendFrames(int channel, int blendFrames) {
idWeapon::Event_GetBlendFrames
===============
*/
-void idWeapon::Event_GetBlendFrames(int channel) {
+void idWeapon::Event_GetBlendFrames(int channel)
+{
idThread::ReturnInt(animBlendFrames);
}
@@ -2926,7 +3466,8 @@ void idWeapon::Event_GetBlendFrames(int channel) {
idWeapon::Event_Next
================
*/
-void idWeapon::Event_Next(void) {
+void idWeapon::Event_Next()
+{
// change to another weapon if possible
owner->NextBestWeapon();
}
@@ -2936,31 +3477,42 @@ void idWeapon::Event_Next(void) {
idWeapon::Event_SetSkin
================
*/
-void idWeapon::Event_SetSkin(const char* skinname) {
- const idDeclSkin* skinDecl;
-
- if (!skinname || !skinname[0]) {
+void idWeapon::Event_SetSkin(const char* skinname)
+{
+ const idDeclSkin* skinDecl = NULL;
+ if (!skinname || !skinname[0])
+ {
skinDecl = NULL;
}
- else {
+ else
+ {
skinDecl = declManager->FindSkin(skinname);
}
+ // Don't update if the skin hasn't changed.
+ if (renderEntity.customSkin == skinDecl && worldModel.GetEntity() != NULL && worldModel.GetEntity()->GetSkin() == skinDecl)
+ {
+ return;
+ }
+
renderEntity.customSkin = skinDecl;
UpdateVisuals();
- if (worldModel.GetEntity()) {
+ if (worldModel.GetEntity())
+ {
worldModel.GetEntity()->SetSkin(skinDecl);
}
- if (gameLocal.isServer) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
-
- msg.Init(msgBuf, sizeof(msgBuf));
- msg.WriteLong((skinDecl != NULL) ? gameLocal.ServerRemapDecl(-1, DECL_SKIN, skinDecl->Index()) : -1);
- ServerSendEvent(EVENT_CHANGESKIN, &msg, false, -1);
- }
+ // Hack, don't send message if flashlight, because clients process the flashlight instantly.
+ //if( gameLocal.isServer && !isPlayerFlashlight )
+ //{
+ // idBitMsg msg;
+ // byte msgBuf[MAX_EVENT_PARAM_SIZE];
+ //
+ // msg.InitWrite( msgBuf, sizeof( msgBuf ) );
+ // msg.WriteLong( ( skinDecl != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_SKIN, skinDecl->Index() ) : -1 );
+ // ServerSendEvent( EVENT_CHANGESKIN, &msg, false );
+ //}
}
/*
@@ -2968,12 +3520,15 @@ void idWeapon::Event_SetSkin(const char* skinname) {
idWeapon::Event_Flashlight
================
*/
-void idWeapon::Event_Flashlight(int enable) {
- if (enable) {
+void idWeapon::Event_Flashlight(int enable)
+{
+ if (enable)
+ {
lightOn = true;
MuzzleFlashLight();
}
- else {
+ else
+ {
lightOn = false;
muzzleFlashEnd = 0;
}
@@ -2984,9 +3539,12 @@ void idWeapon::Event_Flashlight(int enable) {
idWeapon::Event_GetLightParm
================
*/
-void idWeapon::Event_GetLightParm(int parmnum) {
- if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS)) {
+void idWeapon::Event_GetLightParm(int parmnum)
+{
+ if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS))
+ {
gameLocal.Error("shader parm index (%d) out of range", parmnum);
+ return;
}
idThread::ReturnFloat(muzzleFlash.shaderParms[parmnum]);
@@ -2997,9 +3555,12 @@ void idWeapon::Event_GetLightParm(int parmnum) {
idWeapon::Event_SetLightParm
================
*/
-void idWeapon::Event_SetLightParm(int parmnum, float value) {
- if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS)) {
+void idWeapon::Event_SetLightParm(int parmnum, float value)
+{
+ if ((parmnum < 0) || (parmnum >= MAX_ENTITY_SHADER_PARMS))
+ {
gameLocal.Error("shader parm index (%d) out of range", parmnum);
+ return;
}
muzzleFlash.shaderParms[parmnum] = value;
@@ -3012,7 +3573,8 @@ void idWeapon::Event_SetLightParm(int parmnum, float value) {
idWeapon::Event_SetLightParms
================
*/
-void idWeapon::Event_SetLightParms(float parm0, float parm1, float parm2, float parm3) {
+void idWeapon::Event_SetLightParms(float parm0, float parm1, float parm2, float parm3)
+{
muzzleFlash.shaderParms[SHADERPARM_RED] = parm0;
muzzleFlash.shaderParms[SHADERPARM_GREEN] = parm1;
muzzleFlash.shaderParms[SHADERPARM_BLUE] = parm2;
@@ -3026,27 +3588,105 @@ void idWeapon::Event_SetLightParms(float parm0, float parm1, float parm2, float
UpdateVisuals();
}
+/*
+================
+idWeapon::Event_Grabber
+================
+*/
+void idWeapon::Event_Grabber(int enable)
+{
+ if (enable)
+ {
+ grabberState = 0;
+ }
+ else
+ {
+ grabberState = -1;
+ }
+}
+
+/*
+================
+idWeapon::Event_GrabberHasTarget
+================
+*/
+int idWeapon::Event_GrabberHasTarget()
+{
+ return grabberState;
+}
+
+/*
+================
+idWeapon::Event_GrabberSetGrabDistance
+================
+*/
+void idWeapon::Event_GrabberSetGrabDistance(float dist)
+{
+
+}
+
+
/*
================
idWeapon::Event_CreateProjectile
================
*/
-void idWeapon::Event_CreateProjectile(void) {
+idEntity* idWeapon::CreateProjectile()
+{
if (!gameLocal.isClient) {
projectileEnt = NULL;
gameLocal.SpawnEntityDef(projectileDict, &projectileEnt, false);
- if (projectileEnt) {
+ if (projectileEnt)
+ {
projectileEnt->SetOrigin(GetPhysics()->GetOrigin());
projectileEnt->Bind(owner, false);
projectileEnt->Hide();
}
- idThread::ReturnEntity(projectileEnt);
+ return projectileEnt;
}
- else {
- idThread::ReturnEntity(NULL);
+ else
+ {
+ return (NULL);
}
}
+
+/*
+================
+idWeapon::Event_CreateProjectile
+================
+*/
+void idWeapon::Event_CreateProjectile()
+{
+ idThread::ReturnEntity(CreateProjectile());
+}
+
+/*
+================
+idWeapon::GetProjectileLaunchOriginAndAxis
+================
+*/
+void idWeapon::GetProjectileLaunchOriginAndAxis(idVec3& origin, idMat3& axis)
+{
+ assert(owner != NULL);
+
+ // calculate the muzzle position
+ if (barrelJointView != INVALID_JOINT && projectileDict.GetBool("launchFromBarrel"))
+ {
+ // there is an explicit joint for the muzzle
+ // GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
+ GetMuzzlePositionWithHacks(origin, axis);
+ }
+ else
+ {
+ // go straight out of the view
+ origin = playerViewOrigin;
+ axis = playerViewAxis;
+ }
+
+ axis = playerViewAxis; // Fix for plasma rifle not firing correctly on initial shot of a burst fire
+}
+
/*
================
idWeapon::Event_LaunchProjectiles
@@ -3078,12 +3718,20 @@ void idWeapon::Event_LaunchProjectiles(int num_projectiles, float spread, float
// avoid all ammo considerations on an MP client
if (!gameLocal.isClient) {
+#if 1 // previously _D3XP
+
+ int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
+ if ((clipSize != 0) && (ammoClip.Get() <= 0)) {
+ return;
+ }
+
+#else
// check if we're out of ammo or the clip is empty
int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
if (!ammoAvail || ((clipSize != 0) && (ammoClip <= 0))) {
return;
}
-
+#endif
// if this is a power ammo weapon ( currently only the bfg ) then make sure
// we only fire as much power as available in each clip
if (powerAmmo) {
@@ -3092,14 +3740,29 @@ void idWeapon::Event_LaunchProjectiles(int num_projectiles, float spread, float
// in the .def as opposed to just in the script so proper calcs
// can be done here.
dmgPower = (int)dmgPower + 1;
- if (dmgPower > ammoClip) {
- dmgPower = ammoClip;
+ if (dmgPower > ammoClip.Get()) {
+ dmgPower = ammoClip.Get();
}
}
- owner->inventory.UseAmmo(ammoType, (powerAmmo) ? dmgPower : ammoRequired);
+#if 1 // previously _D3XP
+ if (clipSize == 0) {
+ //Weapons with a clip size of 0 launch strait from inventory without moving to a clip
+#endif
+ //In D3XP we used the ammo when the ammo was moved into the clip so we don't want to
+ //use it now.
+ owner->inventory.UseAmmo(ammoType, (powerAmmo) ? dmgPower : ammoRequired);
+
+#if 1 // previously _D3XP
+ }
+#endif
+
if (clipSize && ammoRequired) {
+#if 1 // previously _D3XP
+ ammoClip -= powerAmmo ? dmgPower : ammoRequired;
+#else
ammoClip -= powerAmmo ? dmgPower : 1;
+#endif
}
}
@@ -3213,69 +3876,15 @@ void idWeapon::Event_LaunchProjectiles(int num_projectiles, float spread, float
}
// toss the brass
- PostEventMS(&EV_Weapon_EjectBrass, brassDelay);
+#if 1 // previously _D3XP
+ // if (brassDelay >= 0)
+#endif
+ //PostEventMS(&EV_Weapon_EjectBrass, brassDelay);
}
- // Add a separate short-lived weapon-fire light. Do not use
- // MuzzleFlashLight() here; that path is also used by the flashlight / script
- // weapon light and it should not own normal muzzle-flash firing.
- if (flashJointView != INVALID_JOINT) {
- idVec3 fireViewOrigin;
- idMat3 fireViewAxis;
- idVec3 fireWorldOrigin;
- idMat3 fireWorldAxis;
-
- GetGlobalJointTransform(true, flashJointView, fireViewOrigin, fireViewAxis);
-
- // Keep the view light on the muzzle flash joint, but back it away from
- // walls the same way the old muzzle flash code did so it does not spawn
- // inside opaque geometry.
- idVec3 fireStart = fireViewOrigin - playerViewAxis[0] * 16.0f;
- idVec3 fireEnd = fireViewOrigin + playerViewAxis[0] * 8.0f;
- trace_t fireTr;
- gameLocal.clip.TracePoint(fireTr, fireStart, fireEnd, MASK_SHOT_RENDERMODEL, owner);
- fireViewOrigin = fireTr.endpos - playerViewAxis[0] * 8.0f;
-
- if (flashJointWorld != INVALID_JOINT) {
- GetGlobalJointTransform(false, flashJointWorld, fireWorldOrigin, fireWorldAxis);
- }
- else {
- fireWorldOrigin = fireViewOrigin;
- fireWorldAxis = fireViewAxis;
- }
-
- const char* fireShaderName = weaponDef->dict.GetString("mtr_weaponFireLightShader");
- if (!fireShaderName || !fireShaderName[0]) {
- fireShaderName = weaponDef->dict.GetString("mtr_flashShader");
- }
-
- const idMaterial* fireShader = NULL;
- if (fireShaderName && fireShaderName[0]) {
- fireShader = declManager->FindMaterial(fireShaderName, false);
- }
-
- idVec3 fireColor = weaponDef->dict.GetVector("weaponFireLightColor", flashColor.ToString());
- if (fireColor.x <= 0.0f && fireColor.y <= 0.0f && fireColor.z <= 0.0f) {
- fireColor.Set(1.0f, 0.72f, 0.38f);
- }
-
- float fireIntensity = weaponDef->dict.GetFloat("weaponFireLightIntensity", "1.0");
- if (fireIntensity <= 0.0f) {
- fireIntensity = 1.0f;
- }
- fireColor *= fireIntensity;
-
- float fireRadius = weaponDef->dict.GetFloat("weaponFireLightRadius", va("%f", muzzleFlash.lightRadius[0]));
- if (fireRadius <= 0.0f) {
- fireRadius = 96.0f;
- }
-
- int fireTime = SEC2MS(weaponDef->dict.GetFloat("weaponFireLightTime", weaponDef->dict.GetString("flashTime", "0.08")));
- if (fireTime <= 0) {
- fireTime = 80;
- }
-
- StartWeaponFireMuzzleLight(entityNumber, owner ? owner->entityNumber : -1, fireViewOrigin, fireViewAxis, fireWorldOrigin, fireWorldAxis, fireShader, fireColor, fireRadius, fireTime, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
+ // add the light for the muzzleflash
+ if (!lightOn) {
+ MuzzleFlashLight();
}
owner->WeaponFireFeedback(&weaponDef->dict);
@@ -3284,33 +3893,333 @@ void idWeapon::Event_LaunchProjectiles(int num_projectiles, float spread, float
weaponSmokeStartTime = gameLocal.realClientTime;
}
+/*
+================
+idWeapon::Event_LaunchProjectilesEllipse
+================
+*/
+void idWeapon::Event_LaunchProjectilesEllipse(int num_projectiles, float spreada, float spreadb, float fuseOffset, float power)
+{
+ idProjectile* proj;
+ idEntity* ent;
+ int i;
+ idVec3 dir;
+ float anga, angb;
+ float spin;
+ float distance;
+ trace_t tr;
+ idVec3 start;
+ idVec3 muzzle_pos;
+ idBounds ownerBounds, projBounds;
+
+ if (IsHidden())
+ {
+ return;
+ }
+
+ if (!projectileDict.GetNumKeyVals())
+ {
+ const char* classname = weaponDef->dict.GetString("classname");
+ gameLocal.Warning("No projectile defined on '%s'", classname);
+ return;
+ }
+
+ // avoid all ammo considerations on a client
+ if (!gameLocal.isClient)
+ {
+ if ((clipSize != 0) && (ammoClip.Get() <= 0))
+ {
+ return;
+ }
+
+ if (clipSize == 0)
+ {
+ //Weapons with a clip size of 0 launch strait from inventory without moving to a clip
+ owner->inventory.UseAmmo(ammoType, ammoRequired);
+ }
+
+ if (clipSize && ammoRequired)
+ {
+ ammoClip -= ammoRequired;
+ }
+
+ if (!silent_fire)
+ {
+ // wake up nearby monsters
+ gameLocal.AlertAI(owner);
+ }
+
+ }
+
+ // set the shader parm to the time of last projectile firing,
+ // which the gun material shaders can reference for single shot barrel glows, etc
+ renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
+ renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
+
+ if (worldModel.GetEntity())
+ {
+ worldModel.GetEntity()->SetShaderParm(SHADERPARM_DIVERSITY, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
+ worldModel.GetEntity()->SetShaderParm(SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[SHADERPARM_TIMEOFFSET]);
+ }
+
+ // calculate the muzzle position
+ if (barrelJointView != INVALID_JOINT && projectileDict.GetBool("launchFromBarrel"))
+ {
+ // there is an explicit joint for the muzzle
+ GetGlobalJointTransform(true, barrelJointView, muzzleOrigin, muzzleAxis);
+ }
+ else
+ {
+ // go straight out of the view
+ muzzleOrigin = playerViewOrigin;
+ muzzleAxis = playerViewAxis;
+ }
+
+ // add some to the kick time, incrementally moving repeat firing weapons back
+ if (kick_endtime < gameLocal.time)
+ {
+ kick_endtime = gameLocal.time;
+ }
+ kick_endtime += muzzle_kick_time;
+ if (kick_endtime > gameLocal.time + muzzle_kick_maxtime)
+ {
+ kick_endtime = gameLocal.time + muzzle_kick_maxtime;
+ }
+
+ if (!gameLocal.isClient)
+ {
+ ownerBounds = owner->GetPhysics()->GetAbsBounds();
+
+ owner->AddProjectilesFired(num_projectiles);
+
+ float spreadRadA = DEG2RAD(spreada);
+ float spreadRadB = DEG2RAD(spreadb);
+
+ for (i = 0; i < num_projectiles; i++)
+ {
+ //Ellipse Form
+ spin = (float)DEG2RAD(360.0f) * gameLocal.random.RandomFloat();
+ anga = idMath::Sin(spreadRadA * gameLocal.random.RandomFloat());
+ angb = idMath::Sin(spreadRadB * gameLocal.random.RandomFloat());
+ dir = playerViewAxis[0] + playerViewAxis[2] * (angb * idMath::Sin(spin)) - playerViewAxis[1] * (anga * idMath::Cos(spin));
+ dir.Normalize();
+
+ gameLocal.SpawnEntityDef(projectileDict, &ent);
+ if (ent == NULL || !ent->IsType(idProjectile::Type))
+ {
+ const char* projectileName = weaponDef->dict.GetString("def_projectile");
+ gameLocal.Error("'%s' is not an idProjectile", projectileName);
+ return;
+ }
+
+ proj = static_cast(ent);
+ proj->Create(owner, muzzleOrigin, dir);
+
+ projBounds = proj->GetPhysics()->GetBounds().Rotate(proj->GetPhysics()->GetAxis());
+
+ // make sure the projectile starts inside the bounding box of the owner
+ if (i == 0)
+ {
+ muzzle_pos = muzzleOrigin + playerViewAxis[0] * 2.0f;
+ if ((ownerBounds - projBounds).RayIntersection(muzzle_pos, playerViewAxis[0], distance))
+ {
+ start = muzzle_pos + distance * playerViewAxis[0];
+ }
+ else
+ {
+ start = ownerBounds.GetCenter();
+ }
+ gameLocal.clip.Translation(tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner);
+ muzzle_pos = tr.endpos;
+ }
+
+ proj->Launch(muzzle_pos, dir, pushVelocity, fuseOffset, power);
+ }
+
+ // toss the brass
+ //if( brassDelay >= 0 ) {
+ // PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
+ //}
+ }
+
+ // add the light for the muzzleflash
+ if (!lightOn)
+ {
+ MuzzleFlashLight();
+ }
+
+ owner->WeaponFireFeedback(&weaponDef->dict);
+
+ // reset muzzle smoke
+ weaponSmokeStartTime = gameLocal.time;
+
+}
+
+/**
+* Gives the player a powerup as if it were a weapon shot. It will use the ammo amount specified
+* as ammoRequired.
+*/
+void idWeapon::Event_LaunchPowerup(const char* powerup, float duration, int useAmmo)
+{
+
+ if (IsHidden())
+ {
+ return;
+ }
+
+ // check if we're out of ammo
+ if (useAmmo)
+ {
+ int ammoAvail = owner->inventory.HasAmmo(ammoType, ammoRequired);
+ if (!ammoAvail)
+ {
+ return;
+ }
+ owner->inventory.UseAmmo(ammoType, ammoRequired);
+ }
+
+ // set the shader parm to the time of last projectile firing,
+ // which the gun material shaders can reference for single shot barrel glows, etc
+ renderEntity.shaderParms[SHADERPARM_DIVERSITY] = gameLocal.random.CRandomFloat();
+ renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC(gameLocal.time);
+
+ if (worldModel.GetEntity())
+ {
+ worldModel.GetEntity()->SetShaderParm(SHADERPARM_DIVERSITY, renderEntity.shaderParms[SHADERPARM_DIVERSITY]);
+ worldModel.GetEntity()->SetShaderParm(SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[SHADERPARM_TIMEOFFSET]);
+ }
+
+ // add the light for the muzzleflash
+ if (!lightOn)
+ {
+ MuzzleFlashLight();
+ }
+
+ //owner->Give( powerup, va( "%f", duration ), ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE | ITEM_GIVE_FROM_WEAPON );
+
+
+}
+
+void idWeapon::Event_StartWeaponSmoke()
+{
+
+ // reset muzzle smoke
+ weaponSmokeStartTime = gameLocal.time;
+}
+
+void idWeapon::Event_StopWeaponSmoke()
+{
+
+ // reset muzzle smoke
+ weaponSmokeStartTime = 0;
+}
+
+void idWeapon::Event_StartWeaponParticle(const char* name)
+{
+ WeaponParticle_t* part;
+ weaponParticles.Get(name, &part);
+ if (part)
+ {
+ part->active = true;
+ part->startTime = gameLocal.time;
+
+ //Toggle the emitter
+ if (!part->smoke && part->emitter != NULL)
+ {
+ part->emitter->Show();
+ part->emitter->PostEventMS(&EV_Activate, 0, this);
+ }
+ }
+}
+
+void idWeapon::Event_StopWeaponParticle(const char* name)
+{
+ WeaponParticle_t* part;
+ weaponParticles.Get(name, &part);
+ if (part)
+ {
+ part->active = false;
+ part->startTime = 0;
+
+ //Toggle the emitter
+ if (!part->smoke)
+ {
+ if (part->emitter != NULL)
+ {
+ part->emitter->Hide();
+ part->emitter->PostEventMS(&EV_Activate, 0, this);
+ }
+ }
+ }
+}
+
+void idWeapon::Event_StartWeaponLight(const char* name)
+{
+ WeaponLight_t* light;
+ weaponLights.Get(name, &light);
+ if (light)
+ {
+ light->active = true;
+ light->startTime = gameLocal.time;
+ }
+}
+
+void idWeapon::Event_StopWeaponLight(const char* name)
+{
+ WeaponLight_t* light;
+ weaponLights.Get(name, &light);
+ if (light)
+ {
+ light->active = false;
+ light->startTime = 0;
+ if (light->lightHandle != -1)
+ {
+ gameRenderWorld->FreeLightDef(light->lightHandle);
+ light->lightHandle = -1;
+ }
+ }
+}
/*
=====================
idWeapon::Event_Melee
=====================
*/
-void idWeapon::Event_Melee(void) {
+void idWeapon::Event_Melee()
+{
idEntity* ent;
trace_t tr;
- if (!meleeDef) {
- gameLocal.Error("No meleeDef on '%s'", weaponDef->dict.GetString("classname"));
+ if (weaponDef == NULL)
+ {
+ gameLocal.Error("No weaponDef on '%s'", this->GetName());
+ return;
}
- if (!gameLocal.isClient) {
+ if (meleeDef == NULL)
+ {
+ gameLocal.Error("No meleeDef on '%s'", weaponDef->dict.GetString("classname"));
+ return;
+ }
+
+ if (!gameLocal.isClient)
+ {
idVec3 start = playerViewOrigin;
idVec3 end = start + playerViewAxis[0] * (meleeDistance * owner->PowerUpModifier(MELEE_DISTANCE));
gameLocal.clip.TracePoint(tr, start, end, MASK_SHOT_RENDERMODEL, owner);
- if (tr.fraction < 1.0f) {
+ if (tr.fraction < 1.0f)
+ {
ent = gameLocal.GetTraceEntity(tr);
}
- else {
+ else
+ {
ent = NULL;
}
- if (g_debugWeapon.GetBool()) {
+ if (g_debugWeapon.GetBool())
+ {
gameRenderWorld->DebugLine(colorYellow, start, end, 100);
- if (ent) {
+ if (ent != NULL)
+ {
gameRenderWorld->DebugBounds(colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100);
}
}
@@ -3318,12 +4227,14 @@ void idWeapon::Event_Melee(void) {
bool hit = false;
const char* hitSound = meleeDef->dict.GetString("snd_miss");
- if (ent) {
+ if (ent != NULL)
+ {
float push = meleeDef->dict.GetFloat("push");
idVec3 impulse = -push * owner->PowerUpModifier(SPEED) * tr.c.normal;
- if (gameLocal.world->spawnArgs.GetBool("no_Weapons") && (ent->IsType(idActor::Type) || ent->IsType(idAFAttachment::Type))) {
+ if (gameLocal.world->spawnArgs.GetBool("no_Weapons") && (ent->IsType(idActor::Type) || ent->IsType(idAFAttachment::Type)))
+ {
idThread::ReturnInt(0);
return;
}
@@ -3332,35 +4243,49 @@ void idWeapon::Event_Melee(void) {
// weapon stealing - do this before damaging so weapons are not dropped twice
if (gameLocal.isMultiplayer
- && weaponDef && weaponDef->dict.GetBool("stealing")
+ && weaponDef->dict.GetBool("stealing")
&& ent->IsType(idPlayer::Type)
&& !owner->PowerUpActive(BERSERK)
- && (gameLocal.gameType != GAME_TDM || gameLocal.serverInfo.GetBool("si_teamDamage") || (owner->team != static_cast(ent)->team))
- ) {
+ && ((gameLocal.gameType != GAME_TDM) || gameLocal.serverInfo.GetBool("si_teamDamage") || (owner->team != static_cast(ent)->team))
+ )
+ {
+
owner->StealWeapon(static_cast(ent));
}
- if (ent->fl.takedamage) {
+ if (ent->fl.takedamage)
+ {
idVec3 kickDir, globalKickDir;
meleeDef->dict.GetVector("kickDir", "0 0 0", kickDir);
globalKickDir = muzzleAxis * kickDir;
- ent->Damage(owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier(MELEE_DAMAGE), tr.c.id);
+ //Adjust the melee powerup modifier for the invulnerability boss fight
+ float mod = owner->PowerUpModifier(MELEE_DAMAGE);
+ if (!strcmp(ent->GetEntityDefName(), "monster_hunter_invul"))
+ {
+ //Only do a quater of the damage mod
+ mod *= 0.25f;
+ }
+ ent->Damage(owner, owner, globalKickDir, meleeDefName, mod, tr.c.id);
hit = true;
}
- if (weaponDef->dict.GetBool("impact_damage_effect")) {
+ if (weaponDef->dict.GetBool("impact_damage_effect"))
+ {
- if (ent->spawnArgs.GetBool("bleed")) {
+ if (ent->spawnArgs.GetBool("bleed"))
+ {
hitSound = meleeDef->dict.GetString(owner->PowerUpActive(BERSERK) ? "snd_hit_berserk" : "snd_hit");
ent->AddDamageEffect(tr, impulse, meleeDef->dict.GetString("classname"));
}
- else {
+ else
+ {
int type = tr.c.material->GetSurfaceType();
- if (type == SURFTYPE_NONE) {
+ if (type == SURFTYPE_NONE)
+ {
type = GetDefaultSurfaceType();
}
@@ -3368,20 +4293,24 @@ void idWeapon::Event_Melee(void) {
// start impact sound based on material type
hitSound = meleeDef->dict.GetString(va("snd_%s", materialType));
- if (*hitSound == '\0') {
+ if (*hitSound == '\0')
+ {
hitSound = meleeDef->dict.GetString("snd_metal");
}
- if (gameLocal.time > nextStrikeFx) {
+ if (gameLocal.time > nextStrikeFx)
+ {
const char* decal;
// project decal
decal = weaponDef->dict.GetString("mtr_strike");
- if (decal && *decal) {
+ if (decal != NULL && *decal != NULL)
+ {
gameLocal.ProjectDecal(tr.c.point, -tr.c.normal, 8.0f, true, 6.0, decal);
}
nextStrikeFx = gameLocal.time + 200;
}
- else {
+ else
+ {
hitSound = "";
}
@@ -3392,7 +4321,8 @@ void idWeapon::Event_Melee(void) {
}
}
- if (*hitSound != '\0') {
+ if (*hitSound != '\0')
+ {
const idSoundShader* snd = declManager->FindSound(hitSound);
StartSoundShader(snd, SND_CHANNEL_BODY2, 0, true, NULL);
}
@@ -3411,7 +4341,8 @@ void idWeapon::Event_Melee(void) {
idWeapon::Event_GetWorldModel
=====================
*/
-void idWeapon::Event_GetWorldModel(void) {
+void idWeapon::Event_GetWorldModel()
+{
idThread::ReturnEntity(worldModel.GetEntity());
}
@@ -3420,15 +4351,35 @@ void idWeapon::Event_GetWorldModel(void) {
idWeapon::Event_AllowDrop
=====================
*/
-void idWeapon::Event_AllowDrop(int allow) {
- if (allow) {
+void idWeapon::Event_AllowDrop(int allow)
+{
+ if (allow)
+ {
allowDrop = true;
}
- else {
+ else
+ {
allowDrop = false;
}
}
+/*
+=====================
+idWeapon::CallNativeEvent
+=====================
+*/
+void idWeapon::CallNativeEvent(idStr& name)
+{
+ if (name == "EjectBrass")
+ {
+ Event_EjectBrass();
+ }
+ else
+ {
+ gameLocal.Error("Unknown idWeapon NativeEvent type\n");
+ }
+}
+
/*
================
idWeapon::Event_EjectBrass
@@ -3436,16 +4387,20 @@ idWeapon::Event_EjectBrass
Toss a shell model out from the breach if the bone is present
================
*/
-void idWeapon::Event_EjectBrass(void) {
- if (!g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel()) {
+void idWeapon::Event_EjectBrass()
+{
+ if (!g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel())
+ {
return;
}
- if (ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals()) {
+ if (ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals())
+ {
return;
}
- if (gameLocal.isClient) {
+ if (gameLocal.isClient)
+ {
return;
}
@@ -3453,12 +4408,14 @@ void idWeapon::Event_EjectBrass(void) {
idVec3 origin, linear_velocity, angular_velocity;
idEntity* ent;
- if (!GetGlobalJointTransform(true, ejectJointView, origin, axis)) {
+ if (!GetGlobalJointTransform(true, ejectJointView, origin, axis))
+ {
return;
}
gameLocal.SpawnEntityDef(brassDict, &ent, false);
- if (!ent || !ent->IsType(idDebris::Type)) {
+ if (!ent || !ent->IsType(idDebris::Type))
+ {
gameLocal.Error("'%s' is not an idDebris", weaponDef ? weaponDef->dict.GetString("def_ejectBrass") : "def_ejectBrass");
}
idDebris* debris = static_cast(ent);
@@ -3477,12 +4434,23 @@ void idWeapon::Event_EjectBrass(void) {
idWeapon::Event_IsInvisible
===============
*/
-void idWeapon::Event_IsInvisible(void) {
- if (!owner) {
- idThread::ReturnFloat(0);
- return;
+bool idWeapon::Event_IsInvisible(void)
+{
+ if (!owner)
+ {
+ return false;
}
- idThread::ReturnFloat(owner->PowerUpActive(INVISIBILITY) ? 1 : 0);
+ return owner->PowerUpActive(INVISIBILITY);
+}
+
+/*
+===============
+idWeapon::ClientThink
+===============
+*/
+void idWeapon::ClientThink(const int curTime, const float fraction, const bool predict)
+{
+ UpdateAnimation();
}
/*
@@ -3490,6 +4458,13 @@ void idWeapon::Event_IsInvisible(void) {
idWeapon::ClientPredictionThink
===============
*/
-void idWeapon::ClientPredictionThink(void) {
+void idWeapon::ClientPredictionThink()
+{
UpdateAnimation();
}
+
+
+void idWeapon::ForceAmmoInClip()
+{
+ ammoClip = clipSize;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/Weapon.h b/neo/doom3/game/Weapon.h
index 0e7e28e2..2c9b87ef 100644
--- a/neo/doom3/game/Weapon.h
+++ b/neo/doom3/game/Weapon.h
@@ -1,27 +1,27 @@
/*
===========================================================================
-IceTech GPL Source Code
-Copyright (C) 2026 Justin Marshall
+Doom 3 BFG Edition GPL Source Code
+Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
-This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
+This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
-IceTech Source Code is free software: you can redistribute it and/or modify
+Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
-IceTech Source Code is distributed in the hope that it will be useful,
+Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with IceTech Source Code. If not, see .
+along with Doom 3 BFG Edition Source Code. If not, see .
-In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
+In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
-If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
@@ -29,22 +29,18 @@ If you have questions concerning this license or the applicable additional terms
#ifndef __GAME_WEAPON_H__
#define __GAME_WEAPON_H__
+#include "PredictedValue.h"
+class idWeapon;
+
/*
===============================================================================
Player Weapon
-
+
===============================================================================
*/
-typedef enum {
- WP_READY,
- WP_OUTOFAMMO,
- WP_RELOAD,
- WP_HOLSTERED,
- WP_RISING,
- WP_LOWERING
-} weaponStatus_t;
+extern const idEventDef EV_Weapon_State;
typedef int ammo_t;
static const int AMMO_NUMTYPES = 16;
@@ -56,124 +52,276 @@ static const int LIGHTID_VIEW_MUZZLE_FLASH = 100;
class idMoveableItem;
-class idWeapon : public idAnimatedEntity {
-public:
- CLASS_PROTOTYPE( idWeapon );
+typedef struct
+{
+ char name[64];
+ char particlename[128];
+ bool active;
+ int startTime;
+ jointHandle_t joint; //The joint on which to attach the particle
+ bool smoke; //Is this a smoke particle
+ const idDeclParticle* particle; //Used for smoke particles
+ idFuncEmitter* emitter; //Used for non-smoke particles
+} WeaponParticle_t;
- idWeapon();
+typedef struct
+{
+ char name[64];
+ bool active;
+ int startTime;
+ jointHandle_t joint;
+ int lightHandle;
+ renderLight_t light;
+} WeaponLight_t;
+
+class rvmWeaponObject : public idClass
+{
+public:
+ CLASS_PROTOTYPE(rvmWeaponObject);
+
+ virtual void Init(idWeapon* weapon);
+
+ void SetState(const char* state)
+ {
+ stateThread.SetState(state);
+ }
+ void AppendState(const char* state)
+ {
+ stateThread.PostState(state);
+ }
+ void Execute(void)
+ {
+ stateThread.Execute();
+ }
+ bool IsRunning(void)
+ {
+ return stateThread.IsExecuting();
+ }
+ bool IsStateRunning(const char* name)
+ {
+ return stateThread.CurrentStateIs(name);
+ }
+
+ virtual void OwnerDied(void) {}
+
+ bool IsFiring();
+ bool IsReloading();
+
+ stateResult_t Holstered(stateParms_t* parms)
+ {
+ return SRESULT_WAIT;
+ }
+
+ virtual bool IsHolstered(void)
+ {
+ return IsStateRunning("Holstered");
+ }
+
+protected:
+ idWeapon* owner;
+
+ const idSoundShader* FindSound(const char* name);
+protected:
+ rvStateThread stateThread;
+ float next_attack;
+};
+
+class idWeapon : public idAnimatedEntity
+{
+public:
+ CLASS_PROTOTYPE(idWeapon);
+
+ idWeapon();
virtual ~idWeapon();
// Init
- void Spawn( void );
- void SetOwner( idPlayer *owner );
- idPlayer* GetOwner( void );
- virtual bool ShouldConstructScriptObjectAtSpawn( void ) const;
+ void Spawn();
+ void SetOwner(idPlayer* owner);
+ idPlayer* GetOwner();
+ virtual bool ShouldConstructScriptObjectAtSpawn() const;
+ void SetFlashlightOwner(idPlayer* owner);
- static void CacheWeapon( const char *weaponName );
+ virtual idClass* InvokeChild() override
+ {
+ return currentWeaponObject;
+ }
+
+ static void CacheWeapon(const char* weaponName);
// save games
- void Save( idSaveGame *savefile ) const; // archives object for save game file
- void Restore( idRestoreGame *savefile ); // unarchives object from save game file
+ void Save(idSaveGame* savefile) const; // archives object for save game file
+ void Restore(idRestoreGame* savefile); // unarchives object from save game file
// Weapon definition management
- void Clear( void );
- void GetWeaponDef( const char *objectname, int ammoinclip );
- bool IsLinked( void );
- bool IsWorldModelReady( void );
+ void Clear();
+ void GetWeaponDef(const char* objectname, int ammoinclip);
+ bool IsWorldModelReady();
// GUIs
- const char * Icon( void ) const;
- void UpdateGUI( void );
+ const char* Icon() const;
+ void UpdateGUI();
+ const char* PdaIcon() const;
+ const char* DisplayName() const;
+ const char* Description() const;
- virtual void SetModel( const char *modelname );
- bool GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis );
- void SetPushVelocity( const idVec3 &pushVelocity );
- bool UpdateSkin( void );
+ virtual void SetModel(const char* modelname);
+ bool GetGlobalJointTransform(bool viewModel, const jointHandle_t jointHandle, idVec3& offset, idMat3& axis);
+ void SetPushVelocity(const idVec3& pushVelocity);
+ bool UpdateSkin();
+
+ bool IsFiring()
+ {
+ return isFiring;
+ }
// State control/player interface
- void Think( void );
- void Raise( void );
- void PutAway( void );
- void Reload( void );
- void LowerWeapon( void );
- void RaiseWeapon( void );
- void HideWeapon( void );
- void ShowWeapon( void );
- void HideWorldModel( void );
- void ShowWorldModel( void );
- void OwnerDied( void );
- void BeginAttack( void );
- void EndAttack( void );
- bool IsReady( void ) const;
- bool IsReloading( void ) const;
- bool IsHolstered( void ) const;
- bool ShowCrosshair( void ) const;
- idEntity * DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died );
- bool CanDrop( void ) const;
- void WeaponStolen( void );
-
- // Script state management
- virtual idThread * ConstructScriptObject( void );
- virtual void DeconstructScriptObject( void );
- void SetState( const char *statename, int blendFrames );
- void UpdateScript( void );
- void EnterCinematic( void );
- void ExitCinematic( void );
- void NetCatchup( void );
+ void Think();
+ void Raise();
+ void PutAway();
+ void Reload();
+ void LowerWeapon();
+ void RaiseWeapon();
+ void HideWeapon();
+ void ShowWeapon();
+ void HideWorldModel();
+ void ShowWorldModel();
+ void OwnerDied();
+ void BeginAttack();
+ void EndAttack();
+ bool IsReady() const;
+ bool IsReloading() const;
+ bool IsHolstered() const;
+ bool ShowCrosshair() const;
+ idEntity* DropItem(const idVec3& velocity, int activateDelay, int removeDelay, bool died);
+ bool CanDrop() const;
+ void WeaponStolen();
+ void ForceAmmoInClip();
// Visual presentation
- void PresentWeapon( bool showViewModel );
- int GetZoomFov( void );
- void GetWeaponAngleOffsets( int *average, float *scale, float *max );
- void GetWeaponTimeOffsets( float *time, float *scale );
- bool BloodSplat( float size );
+ void PresentWeapon(bool showViewModel);
+ int GetZoomFov();
+ void GetWeaponAngleOffsets(int* average, float* scale, float* max);
+ void GetWeaponTimeOffsets(float* time, float* scale);
+ bool BloodSplat(float size);
+ void SetIsPlayerFlashlight(bool bl)
+ {
+ isPlayerFlashlight = bl;
+ }
+ void FlashlightOn();
+ void FlashlightOff();
// Ammo
- static ammo_t GetAmmoNumForName( const char *ammoname );
- static const char *GetAmmoNameForNum( ammo_t ammonum );
- static const char *GetAmmoPickupNameForNum( ammo_t ammonum );
- ammo_t GetAmmoType( void ) const;
- int AmmoAvailable( void ) const;
- int AmmoInClip( void ) const;
- void ResetAmmoClip( void );
- int ClipSize( void ) const;
- int LowAmmo( void ) const;
- int AmmoRequired( void ) const;
+ static ammo_t GetAmmoNumForName(const char* ammoname);
+ static const char* GetAmmoNameForNum(ammo_t ammonum);
+ static const char* GetAmmoPickupNameForNum(ammo_t ammonum);
+ ammo_t GetAmmoType() const;
+ int AmmoAvailable() const;
+ int AmmoInClip() const;
+ void ResetAmmoClip();
+ int ClipSize() const;
+ int LowAmmo() const;
+ int AmmoRequired() const;
+ int AmmoCount() const;
+ int GetGrabberState() const;
- virtual void WriteToSnapshot( idBitMsgDelta &msg ) const;
- virtual void ReadFromSnapshot( const idBitMsgDelta &msg );
+ // Flashlight
+ idAnimatedEntity* GetWorldModel()
+ {
+ return worldModel.GetEntity();
+ }
- enum {
+ virtual void WriteToSnapshot(idBitMsg& msg) const;
+ virtual void ReadFromSnapshot(const idBitMsg& msg);
+
+ enum
+ {
EVENT_RELOAD = idEntity::EVENT_MAXEVENTS,
EVENT_ENDRELOAD,
EVENT_CHANGESKIN,
EVENT_MAXEVENTS
};
- virtual bool ClientReceiveEvent( int event, int time, const idBitMsg &msg );
+ virtual bool ClientReceiveEvent(int event, int time, const idBitMsg& msg);
- virtual void ClientPredictionThink( void );
+ virtual void ClientPredictionThink();
+ virtual void ClientThink(const int curTime, const float fraction, const bool predict);
+ void MuzzleFlashLight();
+ void RemoveMuzzleFlashlight();
+ // Get a global origin and axis suitable for the laser sight or bullet tracing
+ // Returns false for hands, grenades, and chainsaw.
+ // Can't be const because a frame may need to be created.
+ bool GetMuzzlePositionWithHacks(idVec3& origin, idMat3& axis);
+
+ void GetProjectileLaunchOriginAndAxis(idVec3& origin, idMat3& axis);
+
+ const idDeclEntityDef* GetDeclEntityDef()
+ {
+ return weaponDef;
+ }
+
+ friend class idPlayer;
+public:
+ virtual void CallNativeEvent(idStr& name) override;
+
+ void Event_SetLightParm(int parmnum, float value);
+ void Event_SetLightParms(float parm0, float parm1, float parm2, float parm3);
+
+ // script events
+ void Event_Clear();
+ void Event_GetOwner();
+ void Event_SetWeaponStatus(float newStatus);
+ void Event_WeaponReady();
+ void Event_WeaponOutOfAmmo();
+ void Event_WeaponReloading();
+ void Event_WeaponHolstered();
+ void Event_WeaponRising();
+ void Event_WeaponLowering();
+ void Event_UseAmmo(int amount);
+ void Event_AddToClip(int amount);
+ void Event_AmmoInClip();
+ int AmmoAvailable();
+ void Event_AmmoAvailable();
+ void Event_TotalAmmoCount();
+ void Event_ClipSize();
+ void Event_PlayAnim(int channel, const char* animname, bool loop);
+ void Event_PlayCycle(int channel, const char* animname);
+ bool Event_AnimDone(int channel, int blendFrames);
+ void Event_SetBlendFrames(int channel, int blendFrames);
+ void Event_GetBlendFrames(int channel);
+ void Event_Next();
+ void Event_SetSkin(const char* skinname);
+ void Event_Flashlight(int enable);
+ void Event_GetLightParm(int parmnum);
+ void Event_LaunchProjectiles(int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower);
+ idEntity* CreateProjectile();
+ void Event_CreateProjectile();
+ void Event_EjectBrass();
+ void Event_Melee();
+ void Event_GetWorldModel();
+ void Event_AllowDrop(int allow);
+ void Event_AutoReload();
+ void Event_NetReload();
+ bool Event_IsInvisible();
+ void Event_NetEndReload();
+
+ void EnterCinematic();
+ void ExitCinematic();
+ void NetCatchup();
+
+ bool IsLinked()
+ {
+ return currentWeaponObject != NULL;
+ }
private:
- // script control
- idScriptBool WEAPON_ATTACK;
- idScriptBool WEAPON_RELOAD;
- idScriptBool WEAPON_NETRELOAD;
- idScriptBool WEAPON_NETENDRELOAD;
- idScriptBool WEAPON_NETFIRING;
- idScriptBool WEAPON_RAISEWEAPON;
- idScriptBool WEAPON_LOWERWEAPON;
- weaponStatus_t status;
- idThread * thread;
- idStr state;
- idStr idealState;
int animBlendFrames;
int animDoneTime;
+ bool isPlayerFlashlight;
bool isLinked;
// precreated projectile
- idEntity *projectileEnt;
+ idEntity* projectileEnt;
- idPlayer * owner;
+ idPlayer* owner;
idEntityPtr worldModel;
// hiding (for GUIs and NPCs)
@@ -186,6 +334,8 @@ private:
bool hide;
bool disabled;
+ bool isFlashLight;
+
// berserk
int berserk;
@@ -196,7 +346,7 @@ private:
// the view weapon render entity parms
idVec3 viewWeaponOrigin;
idMat3 viewWeaponAxis;
-
+
// the muzzle bone's position, used for launching projectiles and trailing smoke
idVec3 muzzleOrigin;
idMat3 muzzleAxis;
@@ -206,14 +356,17 @@ private:
// weapon definition
// we maintain local copies of the projectile and brass dictionaries so they
// do not have to be copied across the DLL boundary when entities are spawned
- const idDeclEntityDef * weaponDef;
- const idDeclEntityDef * meleeDef;
+ const idDeclEntityDef* weaponDef;
+ const idDeclEntityDef* meleeDef;
idDict projectileDict;
float meleeDistance;
idStr meleeDefName;
idDict brassDict;
int brassDelay;
idStr icon;
+ idStr pdaIcon;
+ idStr displayName;
+ idStr itemDesc;
// view weapon gui light
renderLight_t guiLight;
@@ -226,6 +379,9 @@ private:
renderLight_t worldMuzzleFlash; // positioned on world weapon bone
int worldMuzzleFlashHandle;
+ float fraccos;
+ float fraccos2;
+
idVec3 flashColor;
int muzzleFlashEnd;
int flashTime;
@@ -247,15 +403,15 @@ private:
ammo_t ammoType;
int ammoRequired; // amount of ammo to use each shot. 0 means weapon doesn't need ammo.
int clipSize; // 0 means no reload
- int ammoClip;
+ idPredictedValue< int > ammoClip;
int lowAmmo; // if ammo in clip hits this threshold, snd_
bool powerAmmo; // true if the clip reduction is a factor of the power setting when
- // a projectile is launched
+ // a projectile is launched
// mp client
bool isFiring;
// zoom
- int zoomFov; // variable zoom fov per weapon
+ int zoomFov; // variable zoom fov per weapon
// joints from models
jointHandle_t barrelJointView;
@@ -268,29 +424,34 @@ private:
jointHandle_t barrelJointWorld;
jointHandle_t ejectJointWorld;
+ jointHandle_t smokeJointView;
+
+ idHashTable weaponParticles;
+ idHashTable weaponLights;
+
// sound
- const idSoundShader * sndHum;
+ const idSoundShader* sndHum;
// new style muzzle smokes
- const idDeclParticle * weaponSmoke; // null if it doesn't smoke
+ const idDeclParticle* weaponSmoke; // null if it doesn't smoke
int weaponSmokeStartTime; // set to gameLocal.time every weapon fire
bool continuousSmoke; // if smoke is continuous ( chainsaw )
- const idDeclParticle * strikeSmoke; // striking something in melee
- int strikeSmokeStartTime; // timing
- idVec3 strikePos; // position of last melee strike
+ const idDeclParticle* strikeSmoke; // striking something in melee
+ int strikeSmokeStartTime; // timing
+ idVec3 strikePos; // position of last melee strike
idMat3 strikeAxis; // axis of last melee strike
int nextStrikeFx; // used for sound and decal ( may use for strike smoke too )
// nozzle effects
bool nozzleFx; // does this use nozzle effects ( parm5 at rest, parm6 firing )
- // this also assumes a nozzle light atm
+ // this also assumes a nozzle light atm
int nozzleFxFade; // time it takes to fade between the effects
int lastAttack; // last time an attack occured
renderLight_t nozzleGlow; // nozzle light
int nozzleGlowHandle; // handle for nozzle light
idVec3 nozzleGlowColor; // color of the nozzle glow
- const idMaterial * nozzleGlowShader; // shader for glow light
+ const idMaterial* nozzleGlowShader; // shader for glow light
float nozzleGlowRadius; // radius of glow light
// weighting for viewmodel angles
@@ -301,65 +462,44 @@ private:
float weaponOffsetScale;
// flashlight
- void AlertMonsters( void );
+ void AlertMonsters();
// Visual presentation
- void InitWorldModel( const idDeclEntityDef *def );
- void MuzzleFlashLight( void );
- void MuzzleRise( idVec3 &origin, idMat3 &axis );
- void UpdateNozzleFx( void );
- void UpdateFlashPosition( void );
+ void InitWorldModel(const idDeclEntityDef* def);
+ void MuzzleRise(idVec3& origin, idMat3& axis);
+ void UpdateNozzleFx();
+ void UpdateFlashPosition();
- // script events
- void Event_Clear( void );
- void Event_GetOwner( void );
- void Event_WeaponState( const char *statename, int blendFrames );
- void Event_SetWeaponStatus( float newStatus );
- void Event_WeaponReady( void );
- void Event_WeaponOutOfAmmo( void );
- void Event_WeaponReloading( void );
- void Event_WeaponHolstered( void );
- void Event_WeaponRising( void );
- void Event_WeaponLowering( void );
- void Event_UseAmmo( int amount );
- void Event_AddToClip( int amount );
- void Event_AmmoInClip( void );
- void Event_AmmoAvailable( void );
- void Event_TotalAmmoCount( void );
- void Event_ClipSize( void );
- void Event_PlayAnim( int channel, const char *animname );
- void Event_PlayCycle( int channel, const char *animname );
- void Event_AnimDone( int channel, int blendFrames );
- void Event_SetBlendFrames( int channel, int blendFrames );
- void Event_GetBlendFrames( int channel );
- void Event_Next( void );
- void Event_SetSkin( const char *skinname );
- void Event_Flashlight( int enable );
- void Event_GetLightParm( int parmnum );
- void Event_SetLightParm( int parmnum, float value );
- void Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 );
- void Event_LaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower );
- void Event_CreateProjectile( void );
- void Event_EjectBrass( void );
- void Event_Melee( void );
- void Event_GetWorldModel( void );
- void Event_AllowDrop( int allow );
- void Event_AutoReload( void );
- void Event_NetReload( void );
- void Event_IsInvisible( void );
- void Event_NetEndReload( void );
+ //idGrabber grabber;
+ int grabberState;
+public:
+ void Event_Grabber(int enable);
+ int Event_GrabberHasTarget();
+ void Event_GrabberSetGrabDistance(float dist);
+ void Event_LaunchProjectilesEllipse(int num_projectiles, float spreada, float spreadb, float fuseOffset, float power);
+ void Event_LaunchPowerup(const char* powerup, float duration, int useAmmo);
+
+ void Event_StartWeaponSmoke();
+ void Event_StopWeaponSmoke();
+
+ void Event_StartWeaponParticle(const char* name);
+ void Event_StopWeaponParticle(const char* name);
+
+ void Event_StartWeaponLight(const char* name);
+ void Event_StopWeaponLight(const char* name);
+private:
+ rvmWeaponObject* currentWeaponObject;
+ bool OutOfAmmo;
};
-ID_INLINE bool idWeapon::IsLinked( void ) {
- return isLinked;
+ID_INLINE bool idWeapon::IsWorldModelReady()
+{
+ return (worldModel.GetEntity() != NULL);
}
-ID_INLINE bool idWeapon::IsWorldModelReady( void ) {
- return ( worldModel.GetEntity() != NULL );
-}
-
-ID_INLINE idPlayer* idWeapon::GetOwner( void ) {
+ID_INLINE idPlayer* idWeapon::GetOwner()
+{
return owner;
}
-#endif /* !__GAME_WEAPON_H__ */
+#endif /* !__GAME_WEAPON_H__ */
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot.cpp b/neo/doom3/game/bots/Bot.cpp
new file mode 100644
index 00000000..f19cfe83
--- /dev/null
+++ b/neo/doom3/game/bots/Bot.cpp
@@ -0,0 +1,413 @@
+// Bot.cpp
+//
+
+#include "precompiled.h"
+#include "../Game_local.h"
+
+idCVar bot_pathdebug( "bot_pathdebug", "0", CVAR_BOOL | CVAR_CHEAT, "force the bot to path to player" );
+idCVar bot_goaldist( "bot_goaldist", "20", CVAR_INTEGER | CVAR_CHEAT, "" );
+idCVar bot_debugnav( "bot_debugnav", "0", CVAR_BOOL | CVAR_CHEAT, "draws navmesh paths for the bot" );
+idCVar bot_showstate( "bot_showstate", "0", CVAR_BOOL | CVAR_CHEAT, "draws the bot state above the bot" );
+idCVar bot_debug( "bot_debug", "0", CVAR_BOOL, "shows debug info for the bot" );
+idCVar bot_skill("bot_skill", "3", CVAR_INTEGER, "");
+
+CLASS_DECLARATION( idPlayer, rvmBot )
+END_CLASS
+
+/*
+===================
+rvmBot::rvmBot
+===================
+*/
+rvmBot::rvmBot()
+{
+ //bs.action = NULL;
+ hasSpawned = false;
+ gameLocal.RegisterBot( this );
+}
+
+/*
+===================
+rvmBot::~rvmBot
+===================
+*/
+rvmBot::~rvmBot()
+{
+ gameLocal.UnRegisterBot( this );
+}
+
+/*
+==================
+rvmBot::SetEnemy
+==================
+*/
+void rvmBot::SetEnemy( idPlayer* player, idVec3 origin)
+{
+ if(bs.enemy == -1)
+ {
+ bs.enemy = player->entityNumber;
+ bs.aggressiveAttackTime = gameLocal.SysScriptTime() + 2.0f;
+ bs.lastenemyorigin = origin;
+ //bs.action = &botAIBattleRetreat;
+ stateThread.SetState("state_Attacked");
+ }
+}
+
+/*
+==================
+rvmBot::BotUpdateInventory
+==================
+*/
+void rvmBot::BotUpdateInventory( void )
+{
+ bs.inventory[INVENTORY_ARMOR] = inventory.armor;
+ bs.inventory[INVENTORY_GAUNTLET] = 1;
+ bs.inventory[INVENTORY_SHOTGUN] = HasWeapon( weapon_shotgun );
+ bs.inventory[INVENTORY_MACHINEGUN] = HasWeapon( weapon_machinegun );
+ bs.inventory[INVENTORY_GRENADELAUNCHER] = 0;
+ bs.inventory[INVENTORY_ROCKETLAUNCHER] = HasWeapon( weapon_rocketlauncher );
+ bs.inventory[INVENTORY_LIGHTNING] = 0;
+ bs.inventory[INVENTORY_RAILGUN] = 0;
+ bs.inventory[INVENTORY_PLASMAGUN] = HasWeapon( weapon_plasmagun );
+ bs.inventory[INVENTORY_BFG10K] = 0;
+ bs.inventory[INVENTORY_GRAPPLINGHOOK] = 0;
+ bs.inventory[INVENTORY_SHELLS] = inventory.ammo[idWeapon::GetAmmoNumForName( "ammo_shells" )].Get();
+ bs.inventory[INVENTORY_BULLETS] = inventory.ammo[idWeapon::GetAmmoNumForName( "ammo_clip" )].Get();
+ bs.inventory[INVENTORY_GRENADES] = 0;
+ bs.inventory[INVENTORY_CELLS] = inventory.ammo[idWeapon::GetAmmoNumForName( "ammo_cells" )].Get();
+ bs.inventory[INVENTORY_LIGHTNINGAMMO] = 0;
+ bs.inventory[INVENTORY_ROCKETS] = inventory.ammo[idWeapon::GetAmmoNumForName( "ammo_rockets" )].Get();
+ bs.inventory[INVENTORY_SLUGS] = 0;
+ bs.inventory[INVENTORY_BFGAMMO] = 0;
+ bs.inventory[INVENTORY_HEALTH] = health;
+ bs.inventory[INVENTORY_TELEPORTER] = 0;
+ bs.inventory[INVENTORY_MEDKIT] = 0;
+ bs.inventory[INVENTORY_QUAD] = 0;
+ bs.inventory[INVENTORY_ENVIRONMENTSUIT] = 0;
+ bs.inventory[INVENTORY_HASTE] = 0;
+ bs.inventory[INVENTORY_INVISIBILITY] = 0;
+ bs.inventory[INVENTORY_REGEN] = 0;
+ bs.inventory[INVENTORY_FLIGHT] = 0;
+ bs.inventory[INVENTORY_REDFLAG] = 0;
+ bs.inventory[INVENTORY_BLUEFLAG] = 0;
+}
+
+
+/*
+===================
+rvmBot::Spawn
+===================
+*/
+void rvmBot::Spawn( void )
+{
+ idStr botName;
+ char filename[256];
+ int errnum;
+
+ stateThread.SetOwner(this);
+
+ idPlayer::Spawn();
+
+ if( common->IsServer() )
+ {
+ weapon_machinegun = SlotForWeapon( "weapon_machinegun" );
+ weapon_shotgun = SlotForWeapon( "weapon_shotgun" );
+ weapon_plasmagun = SlotForWeapon( "weapon_plasmagun" );
+ weapon_rocketlauncher = SlotForWeapon( "weapon_rocketlauncher" );
+
+ WP_MACHINEGUN = weapon_machinegun;
+ WP_SHOTGUN = weapon_shotgun;
+ WP_PLASMAGUN = weapon_plasmagun;
+ WP_ROCKET_LAUNCHER = weapon_rocketlauncher;
+
+ botName = spawnArgs.GetString( "botname" );
+
+ aas = gameLocal.GetBotAAS();
+ if( aas == NULL )
+ {
+ gameLocal.Error( "Missing AAS\n" );
+ return;
+ }
+
+ // Load in the bot character.
+ bs.character = botCharacterStatsManager.BotLoadCharacterFromFile( va( "bots/%s_c.c", botName.c_str() ), 1 );
+ if( !bs.character )
+ {
+ gameLocal.Error( "Failed to load character file for bot %s\n", botName.c_str() );
+ }
+
+ // Allocate the goal state.
+ bs.gs = botGoalManager.BotAllocGoalState( entityNumber );
+
+ // Get the bot items weights file name.
+ botCharacterStatsManager.Characteristic_String( bs.character, CHARACTERISTIC_ITEMWEIGHTS, filename, 256 );
+ errnum = botGoalManager.BotLoadItemWeights( bs.gs, filename );
+ if( errnum != BLERR_NOERROR )
+ {
+ gameLocal.Error( "Failed to load bot item weights!" );
+ botGoalManager.BotFreeGoalState( bs.gs );
+ return;
+ }
+
+ //allocate a weapon state
+ bs.ws = botWeaponInfoManager.BotAllocWeaponState();
+
+ //load the weapon weights
+ botCharacterStatsManager.Characteristic_String( bs.character, CHARACTERISTIC_WEAPONWEIGHTS, filename, 256 );
+ errnum = botWeaponInfoManager.BotLoadWeaponWeights( bs.ws, filename );
+ if( errnum != BLERR_NOERROR )
+ {
+ // trap_BotFreeGoalState(bs->gs);
+ botWeaponInfoManager.BotFreeWeaponState( bs.ws );
+ return;
+ }
+
+ bs.client = entityNumber;
+ bs.entitynum = entityNumber;
+ bs.setupcount = 4;
+ bs.entergame_time = Bot_Time();
+
+ hasSpawned = true;
+
+ bs.botinput.respawn = true;
+
+ stateThread.SetState("state_Respawn");
+ }
+}
+
+/*
+===================
+rvmBot::Think
+===================
+*/
+void rvmBot::BotMoveToGoalOrigin(idVec3 goalOrigin)
+{
+ bs.botinput.dir = (goalOrigin - firstPersonViewOrigin);
+ idAngles desiredAngles = bs.botinput.dir.ToAngles();
+ if( bs.enemy >= 0 )
+ {
+ idPlayer* enemy = gameLocal.entities[bs.enemy]->Cast();
+ if( enemy )
+ {
+ desiredAngles = (enemy->firstPersonViewOrigin - firstPersonViewOrigin).ToAngles();
+ }
+ }
+
+ bs.botinput.viewangles = desiredAngles;
+
+ bs.botinput.speed = pm_runspeed.GetInteger();
+
+ bs.botinput.dir.Normalize();
+}
+
+/*
+===================
+rvmBot::SpawnToPoint
+===================
+*/
+void rvmBot::SpawnToPoint( const idVec3& spawn_origin, const idAngles& spawn_angles )
+{
+ idPlayer::SpawnToPoint( spawn_origin, spawn_angles );
+
+ if( common->IsServer() )
+ {
+ bs.ltg_time = 0;
+ stateThread.SetState("state_SeekLTG");
+ }
+}
+/*
+===================
+rvmBot::StateThreadChanged
+===================
+*/
+void rvmBot::StateThreadChanged(void) {
+ // Ensure if we are switching states, pop the last goal.
+ bs.ltg_time = 0;
+}
+
+/*
+===================
+rvmBot::ServerThink
+===================
+*/
+void rvmBot::ServerThink( void )
+{
+ bs.origin = GetPhysics()->GetOrigin();
+ bs.eye = GetEyePosition();
+ bs.thinktime = Bot_Time();
+ bs.botinput.actionflags = 0;
+
+ BotUpdateInventory();
+
+ if( bot_pathdebug.IsModified() )
+ {
+ if( bot_pathdebug.GetBool() )
+ {
+ bs.currentGoal.origin = gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin();
+ bs.currentGoal.framenum = gameLocal.framenum;
+ }
+
+ bot_pathdebug.ClearModified();
+ bot_pathdebug.SetBool( false );
+ }
+
+ stateThread.Execute();
+
+ // If we are moving along a set of waypoints, let's move along.
+ aasPath_t path;
+ //int myArea = aas->PointAreaNum(GetOrigin());
+ int goalArea = aas->PointAreaNum( bs.currentGoal.origin );
+ idVec3 org = bs.origin;
+ int curAreaNum = aas->AdjustPositionAndGetArea( org );
+
+ if( bot_debug.GetBool() )
+ {
+ if (bs.useRandomPosition)
+ {
+ aas->ShowWalkPath(GetOrigin(), goalArea, bs.random_move_position);
+ }
+ else
+ {
+ aas->ShowWalkPath(GetOrigin(), goalArea, bs.currentGoal.origin);
+ }
+
+ aas->ShowArea( GetOrigin() );
+ }
+
+ if (bs.useRandomPosition)
+ {
+ aas->WalkPathToGoal(path, curAreaNum, org, goalArea, bs.random_move_position, TFL_WALK | TFL_AIR);
+ }
+ else
+ {
+ aas->WalkPathToGoal(path, curAreaNum, org, goalArea, bs.currentGoal.origin, TFL_WALK | TFL_AIR);
+ }
+
+ idVec3 moveGoal = path.moveGoal;
+ BotMoveToGoalOrigin(path.moveGoal);
+
+ bs.viewangles = bs.botinput.viewangles;
+
+ bs.useRandomPosition = false;
+ bs.attackerEntity = NULL; // Has to be consumed immedaitly.
+ bs.botinput.weapon = bs.weaponnum;
+}
+
+/*
+=======================
+rvmBot::Damage
+=======================
+*/
+void rvmBot::Damage( idEntity* inflictor, idEntity* attacker, const idVec3& dir, const char* damageDefName, const float damageScale, const int location )
+{
+ idPlayer::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
+
+ idPlayer* player = attacker->Cast();
+ if (health <= 0)
+ {
+ if (player)
+ {
+ BotSendChatMessage(DEATH, player->netname );
+ }
+ }
+
+ if (attacker == NULL) {
+ return;
+ }
+
+ //bs.attackerEntity = attacker;
+ SetEnemy(player, attacker->GetOrigin());
+}
+
+/*
+=======================
+rvmBot::InflictedDamageEvent
+=======================
+*/
+void rvmBot::InflictedDamageEvent(idEntity* target) {
+ idPlayer* player = target->Cast();
+
+ // Don't flood the chat with death and insults.
+ if (!player->IsBot() && player->health <= 0)
+ {
+ BotSendChatMessage(KILL, player->netname);
+ }
+}
+
+/*
+=======================
+rvmBot::PresenceTypeBoundingBox
+=======================
+*/
+void rvmBot::PresenceTypeBoundingBox( int presencetype, idVec3& mins, idVec3& maxs )
+{
+ int index;
+ //bounding box size for each presence type
+ //idVec3 boxmins[3] = { {0, 0, 0}, {-15, -15, -24}, {-15, -15, -24} };
+ //idVec3 boxmaxs[3] = { {0, 0, 0}, { 15, 15, 32}, { 15, 15, 8} };
+
+ idVec3 boxmins[3];
+ idVec3 boxmaxs[3];
+
+ boxmins[0] = idVec3( 0, 0, 0 );
+ boxmins[1] = idVec3( -15, -15, -24 );
+ boxmins[2] = idVec3( -15, -15, -24 );
+
+ boxmaxs[0] = idVec3( 0, 0, 0 );
+ boxmaxs[1] = idVec3( 15, 15, 32 );
+ boxmaxs[2] = idVec3( 15, 15, 8 );
+
+
+ if( presencetype == PRESENCE_NORMAL )
+ {
+ index = 1;
+ }
+ else if( presencetype == PRESENCE_CROUCH )
+ {
+ index = 2;
+ }
+ else
+ {
+ //botimport.Print(PRT_FATAL, "AAS_PresenceTypeBoundingBox: unknown presence type\n");
+ index = 2;
+ }
+ mins = boxmins[index];
+ maxs = boxmaxs[index];
+}
+
+
+/*
+===================
+rvmBot::Think
+===================
+*/
+void rvmBot::Think( void )
+{
+ if( !hasSpawned )
+ {
+ return;
+ }
+
+ if( common->IsServer() )
+ {
+ ServerThink();
+
+ if( bot_debug.GetBool() )
+ {
+ idVec4 color;
+ color = idVec4(1, 1, 1, 1);
+ if (bs.enemy >= 0)
+ color = idVec4(1, 0, 0, 1);
+
+ idBounds bounds = idBounds( idVec3( -10, -10, -10 ), idVec3( 10, 10, 10 ) );
+ common->RW()->DebugBounds( color, bounds, GetOrigin() );
+
+ idMat3 axis = viewAngles.ToMat3();
+ common->RW()->DrawTextA(stateThread.GetState()->state.c_str(), GetOrigin(), 1.0f, color, axis);
+ }
+ }
+
+ deltaViewAngles.Zero();
+
+ idPlayer::Think();
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot.h b/neo/doom3/game/bots/Bot.h
new file mode 100644
index 00000000..5a85c64d
--- /dev/null
+++ b/neo/doom3/game/bots/Bot.h
@@ -0,0 +1,920 @@
+// Bot.h
+//
+
+class rvmBotAIBotActionBase;
+
+// These need to match items.def
+#define INVENTORY_ARMOR 1
+#define INVENTORY_GAUNTLET 4
+#define INVENTORY_SHOTGUN 5
+#define INVENTORY_MACHINEGUN 6
+#define INVENTORY_GRENADELAUNCHER 7
+#define INVENTORY_ROCKETLAUNCHER 8
+#define INVENTORY_LIGHTNING 9
+#define INVENTORY_RAILGUN 10
+#define INVENTORY_PLASMAGUN 11
+#define INVENTORY_BFG10K 13
+#define INVENTORY_GRAPPLINGHOOK 14
+#define INVENTORY_NAILGUN 15
+#define INVENTORY_PROXLAUNCHER 16
+#define INVENTORY_CHAINGUN 17
+#define INVENTORY_SHELLS 18
+#define INVENTORY_BULLETS 19
+#define INVENTORY_GRENADES 20
+#define INVENTORY_CELLS 21
+#define INVENTORY_LIGHTNINGAMMO 22
+#define INVENTORY_ROCKETS 23
+#define INVENTORY_SLUGS 24
+#define INVENTORY_BFGAMMO 25
+#define INVENTORY_NAILS 26
+#define INVENTORY_MINES 27
+#define INVENTORY_BELT 28
+#define INVENTORY_HEALTH 29
+#define INVENTORY_TELEPORTER 30
+#define INVENTORY_MEDKIT 31
+#define INVENTORY_KAMIKAZE 32
+#define INVENTORY_PORTAL 33
+#define INVENTORY_INVULNERABILITY 34
+#define INVENTORY_QUAD 35
+#define INVENTORY_ENVIRONMENTSUIT 36
+#define INVENTORY_HASTE 37
+#define INVENTORY_INVISIBILITY 38
+#define INVENTORY_REGEN 39
+#define INVENTORY_FLIGHT 40
+#define INVENTORY_SCOUT 41
+#define INVENTORY_GUARD 42
+#define INVENTORY_DOUBLER 43
+#define INVENTORY_AMMOREGEN 44
+#define INVENTORY_REDFLAG 45
+#define INVENTORY_BLUEFLAG 46
+#define INVENTORY_NEUTRALFLAG 47
+#define INVENTORY_REDCUBE 48
+#define INVENTORY_BLUECUBE 49
+#define INVENTORY_DOUBLESHOTGUN 50
+
+#define MODELINDEX_DEFAULT 0
+#define MODELINDEX_ARMORSHARD 1
+#define MODELINDEX_ARMORCOMBAT 2
+#define MODELINDEX_ARMORBODY 3
+#define MODELINDEX_HEALTHSMALL 4
+#define MODELINDEX_HEALTH 5
+#define MODELINDEX_HEALTHLARGE 6
+#define MODELINDEX_HEALTHMEGA 7
+#define MODELINDEX_GAUNTLET 8
+#define MODELINDEX_SHOTGUN 9
+#define MODELINDEX_MACHINEGUN 10
+#define MODELINDEX_GRENADELAUNCHER 11
+#define MODELINDEX_ROCKETLAUNCHER 12
+#define MODELINDEX_LIGHTNING 13
+#define MODELINDEX_RAILGUN 14
+#define MODELINDEX_PLASMAGUN 15
+#define MODELINDEX_BFG10K 16
+#define MODELINDEX_GRAPPLINGHOOK 17
+#define MODELINDEX_SHELLS 18
+#define MODELINDEX_BULLETS 19
+#define MODELINDEX_GRENADES 20
+#define MODELINDEX_CELLS 21
+#define MODELINDEX_LIGHTNINGAMMO 22
+#define MODELINDEX_ROCKETS 23
+#define MODELINDEX_SLUGS 24
+#define MODELINDEX_BFGAMMO 25
+#define MODELINDEX_TELEPORTER 26
+#define MODELINDEX_MEDKIT 27
+#define MODELINDEX_QUAD 28
+#define MODELINDEX_ENVIRONMENTSUIT 29
+#define MODELINDEX_HASTE 30
+#define MODELINDEX_INVISIBILITY 31
+#define MODELINDEX_REGEN 32
+#define MODELINDEX_FLIGHT 33
+#define MODELINDEX_REDFLAG 34
+#define MODELINDEX_BLUEFLAG 35
+#define MODELINDEX_KAMIKAZE 36
+#define MODELINDEX_PORTAL 37
+#define MODELINDEX_INVULNERABILITY 38
+#define MODELINDEX_NAILS 39
+#define MODELINDEX_MINES 40
+#define MODELINDEX_BELT 41
+#define MODELINDEX_SCOUT 42
+#define MODELINDEX_GUARD 43
+#define MODELINDEX_DOUBLER 44
+#define MODELINDEX_AMMOREGEN 45
+#define MODELINDEX_NEUTRALFLAG 46
+#define MODELINDEX_REDCUBE 47
+#define MODELINDEX_BLUECUBE 48
+#define MODELINDEX_NAILGUN 49
+#define MODELINDEX_PROXLAUNCHER 50
+#define MODELINDEX_CHAINGUN 51
+#define MODELINDEX_POINTABLUE 52
+#define MODELINDEX_POINTBBLUE 53
+#define MODELINDEX_POINTARED 54
+#define MODELINDEX_POINTBRED 55
+#define MODELINDEX_POINTAWHITE 56
+#define MODELINDEX_POINTBWHITE 57
+#define MODELINDEX_POINTWHITE 58
+#define MODELINDEX_POINTRED 59
+#define MODELINDEX_POINTBLUE 60
+#define WEAPONINDEX_GAUNTLET 1
+#define WEAPONINDEX_MACHINEGUN 2
+#define WEAPONINDEX_SHOTGUN 3
+#define WEAPONINDEX_GRENADE_LAUNCHER 4
+#define WEAPONINDEX_ROCKET_LAUNCHER 5
+#define WEAPONINDEX_LIGHTNING 6
+#define WEAPONINDEX_RAILGUN 7
+#define WEAPONINDEX_PLASMAGUN 8
+#define WEAPONINDEX_BFG 9
+#define WEAPONINDEX_GRAPPLING_HOOK 10
+#define WEAPONINDEX_NAILGUN 11
+#define WEAPONINDEX_PROXLAUNCHER 12
+#define WEAPONINDEX_CHAINGUN 13
+
+//enemy stuff
+#define ENEMY_HORIZONTAL_DIST 200
+#define ENEMY_HEIGHT 201
+
+#define MAX_AVOIDGOALS 256
+#define MAX_GOALSTACK 8
+
+#define GFL_NONE 0
+#define GFL_ITEM 1
+#define GFL_ROAM 2
+#define GFL_DROPPED 4
+
+#define BLERR_NOERROR 0 //no error
+#define BLERR_LIBRARYNOTSETUP 1 //library not setup
+#define BLERR_INVALIDENTITYNUMBER 2 //invalid entity number
+#define BLERR_NOAASFILE 3 //no AAS file available
+#define BLERR_CANNOTOPENAASFILE 4 //cannot open AAS file
+#define BLERR_WRONGAASFILEID 5 //incorrect AAS file id
+#define BLERR_WRONGAASFILEVERSION 6 //incorrect AAS file version
+#define BLERR_CANNOTREADAASLUMP 7 //cannot read AAS file lump
+#define BLERR_CANNOTLOADICHAT 8 //cannot load initial chats
+#define BLERR_CANNOTLOADITEMWEIGHTS 9 //cannot load item weights
+#define BLERR_CANNOTLOADITEMCONFIG 10 //cannot load item config
+#define BLERR_CANNOTLOADWEAPONWEIGHTS 11 //cannot load weapon weights
+#define BLERR_CANNOTLOADWEAPONCONFIG 12 //cannot load weapon config
+
+#define WT_BALANCE 1
+#define MAX_WEIGHTS 128
+
+//fuzzy seperator
+struct fuzzyseperator_t
+{
+ fuzzyseperator_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ inUse = false;
+ index = 0;
+ value = 0;
+ type = 0;
+ weight = 0;
+ minweight = 0.0f;
+ maxweight = 0.0f;
+ child = nullptr;
+ next = nullptr;
+ }
+
+ bool inUse;
+ int index;
+ int value;
+ int type;
+ float weight;
+ float minweight;
+ float maxweight;
+ fuzzyseperator_t* child;
+ fuzzyseperator_t* next;
+};
+
+//fuzzy weight
+struct weight_t
+{
+ weight_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ name.Clear();
+ firstseperator = nullptr;
+ }
+
+ idStr name;
+ fuzzyseperator_t* firstseperator;
+};
+
+//weight configuration
+struct weightconfig_t
+{
+ weightconfig_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ inUse = false;
+ numweights = 0;
+ filename.Clear();
+
+ for( int i = 0; i < MAX_WEIGHTS; i++ )
+ {
+ weights[i].Reset();
+ }
+ }
+
+ bool inUse;
+ int numweights;
+ weight_t weights[MAX_WEIGHTS];
+ idStr filename;
+};
+
+// ------------------------------------------------------------------------------------
+
+#define BOTFILESBASEFOLDER "botfiles"
+//debug line colors
+#define LINECOLOR_NONE -1
+#define LINECOLOR_RED 1//0xf2f2f0f0L
+#define LINECOLOR_GREEN 2//0xd0d1d2d3L
+#define LINECOLOR_BLUE 3//0xf3f3f1f1L
+#define LINECOLOR_YELLOW 4//0xdcdddedfL
+#define LINECOLOR_ORANGE 5//0xe0e1e2e3L
+
+//Print types
+#define PRT_MESSAGE 1
+#define PRT_WARNING 2
+#define PRT_ERROR 3
+#define PRT_FATAL 4
+#define PRT_EXIT 5
+
+//console message types
+#define CMS_NORMAL 0
+#define CMS_CHAT 1
+
+//action flags
+#define ACTION_ATTACK 0x0000001
+#define ACTION_USE 0x0000002
+#define ACTION_RESPAWN 0x0000008
+#define ACTION_JUMP 0x0000010
+#define ACTION_MOVEUP 0x0000020
+#define ACTION_CROUCH 0x0000080
+#define ACTION_MOVEDOWN 0x0000100
+#define ACTION_MOVEFORWARD 0x0000200
+#define ACTION_MOVEBACK 0x0000800
+#define ACTION_MOVELEFT 0x0001000
+#define ACTION_MOVERIGHT 0x0002000
+#define ACTION_DELAYEDJUMP 0x0008000
+#define ACTION_TALK 0x0010000
+#define ACTION_GESTURE 0x0020000
+#define ACTION_WALK 0x0080000
+#define ACTION_AFFIRMATIVE 0x0100000
+#define ACTION_NEGATIVE 0x0200000
+#define ACTION_GETFLAG 0x0800000
+#define ACTION_GUARDBASE 0x1000000
+#define ACTION_PATROL 0x2000000
+#define ACTION_FOLLOWME 0x8000000
+
+
+//#define DEBUG
+#define CTF
+
+#define MAX_ITEMS 256
+//bot flags
+#define BFL_STRAFERIGHT 1 //strafe to the right
+#define BFL_ATTACKED 2 //bot has attacked last ai frame
+#define BFL_ATTACKJUMPED 4 //bot jumped during attack last frame
+#define BFL_AIMATENEMY 8 //bot aimed at the enemy this frame
+#define BFL_AVOIDRIGHT 16 //avoid obstacles by going to the right
+#define BFL_IDEALVIEWSET 32 //bot has ideal view angles set
+#define BFL_FIGHTSUICIDAL 64 //bot is in a suicidal fight
+//long term goal types
+#define LTG_TEAMHELP 1 //help a team mate
+#define LTG_TEAMACCOMPANY 2 //accompany a team mate
+#define LTG_DEFENDKEYAREA 3 //defend a key area
+#define LTG_GETFLAG 4 //get the enemy flag
+#define LTG_RUSHBASE 5 //rush to the base
+#define LTG_RETURNFLAG 6 //return the flag
+#define LTG_CAMP 7 //camp somewhere
+#define LTG_CAMPORDER 8 //ordered to camp somewhere
+#define LTG_PATROL 9 //patrol
+#define LTG_GETITEM 10 //get an item
+#define LTG_KILL 11 //kill someone
+#define LTG_HARVEST 12 //harvest skulls
+#define LTG_ATTACKENEMYBASE 13 //attack the enemy base
+#define LTG_MAKELOVE_UNDER 14
+#define LTG_MAKELOVE_ONTOP 15
+//some goal dedication times
+#define TEAM_HELP_TIME 60 //1 minute teamplay help time
+#define TEAM_ACCOMPANY_TIME 600 //10 minutes teamplay accompany time
+#define TEAM_DEFENDKEYAREA_TIME 600 //10 minutes ctf defend base time
+#define TEAM_CAMP_TIME 600 //10 minutes camping time
+#define TEAM_PATROL_TIME 600 //10 minutes patrolling time
+#define TEAM_LEAD_TIME 600 //10 minutes taking the lead
+#define TEAM_GETITEM_TIME 60 //1 minute
+#define TEAM_KILL_SOMEONE 180 //3 minute to kill someone
+#define TEAM_ATTACKENEMYBASE_TIME 600 //10 minutes
+#define TEAM_HARVEST_TIME 120 //2 minutes
+#define CTF_GETFLAG_TIME 600 //10 minutes ctf get flag time
+#define CTF_RUSHBASE_TIME 120 //2 minutes ctf rush base time
+#define CTF_RETURNFLAG_TIME 180 //3 minutes to return the flag
+#define CTF_ROAM_TIME 60 //1 minute ctf roam time
+//patrol flags
+#define PATROL_LOOP 1
+#define PATROL_REVERSE 2
+#define PATROL_BACK 4
+//teamplay task preference
+#define TEAMTP_DEFENDER 1
+#define TEAMTP_ATTACKER 2
+//CTF strategy
+#define CTFS_AGRESSIVE 1
+//copied from the aas file header
+#define PRESENCE_NONE 1
+#define PRESENCE_NORMAL 2
+#define PRESENCE_CROUCH 4
+//
+#define MAX_PROXMINES 64
+
+
+#define MAX_CHARACTERISTICS 80
+
+#define CT_INTEGER 1
+#define CT_FLOAT 2
+#define CT_STRING 3
+
+#define DEFAULT_CHARACTER "bots/default_c.c"
+
+#define MAX_AVOIDGOALS 256
+#define MAX_GOALSTACK 8
+
+#define GFL_NONE 0
+#define GFL_ITEM 1
+#define GFL_ROAM 2
+#define GFL_DROPPED 4
+#define MAX_EPAIRKEY 128
+
+//characteristic value
+struct cvalue
+{
+ cvalue()
+ {
+ integer = 0;
+ _float = 0.0f;
+ string = "";
+ }
+
+ int integer;
+ float _float;
+ idStr string;
+};
+
+//a characteristic
+struct bot_characteristic_t
+{
+ bot_characteristic_t()
+ {
+ type = 0;
+ }
+
+ char type; //characteristic type
+ cvalue value; //characteristic value
+};
+
+//a bot character
+struct bot_character_t
+{
+ bot_character_t()
+ {
+ filename = "";
+ inUse = false;
+ skill = 0.0f;
+ }
+
+ idStr filename;
+ bool inUse;
+ float skill;
+ bot_characteristic_t c[MAX_CHARACTERISTICS];
+};
+
+//the bot input, will be converted to an usercmd_t
+//the bot input, will be converted to an usercmd_t
+struct bot_input_t
+{
+ bot_input_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ thinktime = 0;
+ dir.Zero();
+ speed = 0;
+ viewangles.Zero();
+ actionflags = 0;
+ weapon = 0;
+ lastWeaponNum = 0;
+ respawn = false;
+ }
+
+ float thinktime; //time since last output (in seconds)
+ idVec3 dir; //movement direction
+ float speed; //speed in the range [0, 400]
+ idAngles viewangles; //the view angles
+ int actionflags; //one of the ACTION_? flags
+ int weapon; //weapon to use
+ int lastWeaponNum;
+ bool respawn;
+};
+
+#if 0
+//entity state
+typedef struct bot_entitystate_s
+{
+ int type; // entity type
+ int flags; // entity flags
+ vec3_t origin; // origin of the entity
+ vec3_t angles; // angles of the model
+ vec3_t old_origin; // for lerping
+ vec3_t mins; // bounding box minimums
+ vec3_t maxs; // bounding box maximums
+ int groundent; // ground entity
+ int solid; // solid type
+ int modelindex; // model used
+ int modelindex2; // weapons, CTF flags, etc
+ int frame; // model frame number
+ int event; // impulse events -- muzzle flashes, footsteps, etc
+ int eventParm; // even parameter
+ int powerups; // bit flags
+ int weapon; // determines weapon and flash model, etc
+ int legsAnim; // mask off ANIM_TOGGLEBIT
+ int torsoAnim; // mask off ANIM_TOGGLEBIT
+} bot_entitystate_t;
+
+//check points
+typedef struct bot_waypoint_s
+{
+ int inuse;
+ char name[32];
+ bot_goal_t goal;
+ struct bot_waypoint_s* next, * prev;
+} bot_waypoint_t;
+
+//bot settings
+typedef struct bot_settings_s
+{
+ char characterfile[MAX_QPATH];
+ float skill;
+ char team[MAX_QPATH];
+} bot_settings_t;
+
+#define MAX_ACTIVATESTACK 8
+#define MAX_ACTIVATEAREAS 32
+
+typedef struct bot_activategoal_s
+{
+ int inuse;
+ bot_goal_t goal; //goal to activate (buttons etc.)
+ float time; //time to activate something
+ float start_time; //time starting to activate something
+ float justused_time; //time the goal was used
+ int shoot; //true if bot has to shoot to activate
+ int weapon; //weapon to be used for activation
+ vec3_t target; //target to shoot at to activate something
+ vec3_t origin; //origin of the blocking entity to activate
+ int areas[MAX_ACTIVATEAREAS]; //routing areas disabled by blocking entity
+ int numareas; //number of disabled routing areas
+ int areasdisabled; //true if the areas are disabled for the routing
+ struct bot_activategoal_s* next; //next activate goal on stack
+} bot_activategoal_t;
+
+//bot state
+typedef struct bot_state_s
+{
+ int inuse; //true if this state is used by a bot client
+ int botthink_residual; //residual for the bot thinks
+ int client; //client number of the bot
+ int entitynum; //entity number of the bot
+ playerState_t cur_ps; //current player state
+ int last_eFlags; //last ps flags
+ usercmd_t lastucmd; //usercmd from last frame
+ int entityeventTime[1024]; //last entity event time
+ //
+ bot_settings_t settings; //several bot settings
+ int ( *ainode )( struct bot_state_s* bs ); //current AI node
+ float thinktime; //time the bot thinks this frame
+ vec3_t origin; //origin of the bot
+ vec3_t velocity; //velocity of the bot
+ int presencetype; //presence type of the bot
+ vec3_t eye; //eye coordinates of the bot
+ int areanum; //the number of the area the bot is in
+ int inventory[MAX_ITEMS]; //string with items amounts the bot has
+ int tfl; //the travel flags the bot uses
+ int flags; //several flags
+ int respawn_wait; //wait until respawned
+ int lasthealth; //health value previous frame
+ int lastkilledplayer; //last killed player
+ int lastkilledby; //player that last killed this bot
+ int botdeathtype; //the death type of the bot
+ int enemydeathtype; //the death type of the enemy
+ int botsuicide; //true when the bot suicides
+ int enemysuicide; //true when the enemy of the bot suicides
+ int setupcount; //true when the bot has just been setup
+ int map_restart; //true when the map is being restarted
+ int entergamechat; //true when the bot used an enter game chat
+ int num_deaths; //number of time this bot died
+ int num_kills; //number of kills of this bot
+ int revenge_enemy; //the revenge enemy
+ int revenge_kills; //number of kills the enemy made
+ int lastframe_health; //health value the last frame
+ int lasthitcount; //number of hits last frame
+ int chatto; //chat to all or team
+ float walker; //walker charactertic
+ float ltime; //local bot time
+ float entergame_time; //time the bot entered the game
+ float ltg_time; //long term goal time
+ float nbg_time; //nearby goal time
+ float respawn_time; //time the bot takes to respawn
+ float respawnchat_time; //time the bot started a chat during respawn
+ float chase_time; //time the bot will chase the enemy
+ float enemyvisible_time; //time the enemy was last visible
+ float check_time; //time to check for nearby items
+ float stand_time; //time the bot is standing still
+ float lastchat_time; //time the bot last selected a chat
+ float kamikaze_time; //time to check for kamikaze usage
+ float invulnerability_time; //time to check for invulnerability usage
+ float standfindenemy_time; //time to find enemy while standing
+ float attackstrafe_time; //time the bot is strafing in one dir
+ float attackcrouch_time; //time the bot will stop crouching
+ float attackchase_time; //time the bot chases during actual attack
+ float attackjump_time; //time the bot jumped during attack
+ float enemysight_time; //time before reacting to enemy
+ float enemydeath_time; //time the enemy died
+ float enemyposition_time; //time the position and velocity of the enemy were stored
+ float defendaway_time; //time away while defending
+ float defendaway_range; //max travel time away from defend area
+ float rushbaseaway_time; //time away from rushing to the base
+ float attackaway_time; //time away from attacking the enemy base
+ float harvestaway_time; //time away from harvesting
+ float ctfroam_time; //time the bot is roaming in ctf
+ float killedenemy_time; //time the bot killed the enemy
+ float arrive_time; //time arrived (at companion)
+ float lastair_time; //last time the bot had air
+ float teleport_time; //last time the bot teleported
+ float camp_time; //last time camped
+ float camp_range; //camp range
+ float weaponchange_time; //time the bot started changing weapons
+ float firethrottlewait_time; //amount of time to wait
+ float firethrottleshoot_time; //amount of time to shoot
+ float notblocked_time; //last time the bot was not blocked
+ float blockedbyavoidspot_time; //time blocked by an avoid spot
+ float predictobstacles_time; //last time the bot predicted obstacles
+ int predictobstacles_goalareanum; //last goal areanum the bot predicted obstacles for
+ vec3_t aimtarget;
+ vec3_t enemyvelocity; //enemy velocity 0.5 secs ago during battle
+ vec3_t enemyorigin; //enemy origin 0.5 secs ago during battle
+ //
+ int kamikazebody; //kamikaze body
+ int proxmines[MAX_PROXMINES];
+ int numproxmines;
+ //
+ bot_character_t* character; //the bot character
+ int ms; //move state of the bot
+ int gs; //goal state of the bot
+ int cs; //chat state of the bot
+ int ws; //weapon state of the bot
+ //
+ int enemy; //enemy entity number
+ int lastenemyareanum; //last reachability area the enemy was in
+ vec3_t lastenemyorigin; //last origin of the enemy in the reachability area
+ int weaponnum; //current weapon number
+ vec3_t viewangles; //current view angles
+ vec3_t ideal_viewangles; //ideal view angles
+ vec3_t viewanglespeed;
+ //
+ int ltgtype; //long term goal type
+ // team goals
+ int teammate; //team mate involved in this team goal
+ int decisionmaker; //player who decided to go for this goal
+ int ordered; //true if ordered to do something
+ float order_time; //time ordered to do something
+ int owndecision_time; //time the bot made it's own decision
+ bot_goal_t teamgoal; //the team goal
+ bot_goal_t altroutegoal; //alternative route goal
+ float reachedaltroutegoal_time; //time the bot reached the alt route goal
+ float teammessage_time; //time to message team mates what the bot is doing
+ float teamgoal_time; //time to stop helping team mate
+ float teammatevisible_time; //last time the team mate was NOT visible
+ int teamtaskpreference; //team task preference
+ // last ordered team goal
+ int lastgoal_decisionmaker;
+ int lastgoal_ltgtype;
+ int lastgoal_teammate;
+ bot_goal_t lastgoal_teamgoal;
+ // for leading team mates
+ int lead_teammate; //team mate the bot is leading
+ bot_goal_t lead_teamgoal; //team goal while leading
+ float lead_time; //time leading someone
+ float leadvisible_time; //last time the team mate was visible
+ float leadmessage_time; //last time a messaged was sent to the team mate
+ float leadbackup_time; //time backing up towards team mate
+ //
+ char teamleader[32]; //netname of the team leader
+ float askteamleader_time; //time asked for team leader
+ float becometeamleader_time; //time the bot will become the team leader
+ float teamgiveorders_time; //time to give team orders
+ float lastflagcapture_time; //last time a flag was captured
+ int numteammates; //number of team mates
+ int redflagstatus; //0 = at base, 1 = not at base
+ int blueflagstatus; //0 = at base, 1 = not at base
+ int neutralflagstatus; //0 = at base, 1 = our team has flag, 2 = enemy team has flag, 3 = enemy team dropped the flag
+ int flagstatuschanged; //flag status changed
+ int forceorders; //true if forced to give orders
+ int flagcarrier; //team mate carrying the enemy flag
+ int ctfstrategy; //ctf strategy
+ char subteam[32]; //sub team name
+ float formation_dist; //formation team mate intervening space
+ char formation_teammate[16]; //netname of the team mate the bot uses for relative positioning
+ float formation_angle; //angle relative to the formation team mate
+ vec3_t formation_dir; //the direction the formation is moving in
+ vec3_t formation_origin; //origin the bot uses for relative positioning
+ bot_goal_t formation_goal; //formation goal
+
+ bot_activategoal_t* activatestack; //first activate goal on the stack
+ bot_activategoal_t activategoalheap[MAX_ACTIVATESTACK]; //activate goal heap
+
+ bot_waypoint_t* checkpoints; //check points
+ bot_waypoint_t* patrolpoints; //patrol points
+ bot_waypoint_t* curpatrolpoint; //current patrol point the bot is going for
+ int patrolflags; //patrol flags
+
+// jmarshall
+ bot_goal_t currentgoal;
+
+ vec3_t currentMoveGoal;
+ vec3_t movement_waypoints[NAV_MAX_PATHSTEPS];
+ int numMovementWaypoints;
+ int currentWaypoint;
+
+ vec3_t last_origin;
+ vec3_t very_short_term_origin;
+
+ int stuck_time;
+
+ vec3_t last_enemy_visible_position;
+ vec3_t random_move_position;
+ // jmarshall end
+
+ bot_input_t input;
+} bot_state_t;
+
+void BotUpdateInput( bot_state_t* bs, int time, int elapsed_time );
+qboolean BotIsDead( bot_state_t* bs );
+
+void AIEnter_Respawn( bot_state_t* bs, char* s );
+
+extern float floattime;
+#define FloatTime() floattime
+
+
+float Characteristic_BFloat( bot_character_t* ch, int index, float min, float max );
+void Characteristic_String( bot_character_t* ch, int index, char* buf, int size );
+
+bot_character_t* BotLoadCharacterFromFile( char* charfile, int skill );
+
+void BotInitLevelItems( void );
+
+void BotChooseWeapon( bot_state_t* bs );
+
+inline float AAS_Time()
+{
+ return floattime;
+}
+
+unsigned short int BotTravelTime( vec3_t start, vec3_t end );
+#endif
+
+#define MAX_BOT_INVENTORY 256
+
+typedef enum
+{
+ NULLMOVEFLAG = -1,
+ MOVE_PRONE,
+ MOVE_CROUCH,
+ MOVE_WALK,
+ MOVE_RUN2,
+ MOVE_SPRINT,
+ MOVE_JUMP,
+} botMoveFlags_t;
+
+//
+// rvmBotUtil
+//
+class rvmBotUtil
+{
+public:
+ static float random()
+ {
+ return ( ( rand() & 0x7fff ) / ( ( float )0x7fff ) );
+ }
+
+ static float crandom()
+ {
+ return ( 2.0 * ( random() - 0.5 ) );
+ }
+};
+
+#include "Bot_char.h"
+#include "Bot_weights.h"
+#include "Bot_weapons.h"
+#include "Bot_goal.h"
+
+struct bot_state_t
+{
+ bot_state_t()
+ {
+ character = NULL;
+ gs = 0;
+ ws = 0;
+ Reset();
+ }
+ void Reset()
+ {
+ attackerEntity = NULL;
+ client = 0;
+ entitynum = 0;
+ setupcount = 0;
+ entergame_time = 0;
+ weaponnum = 0;
+ lasthealth = 0;
+ ltg_time = 0;
+ weaponchange_time = 0;
+ enemy = 0;
+ enemyvisible_time = 0;
+ enemysuicide = 0;
+ enemysight_time = 0;
+ check_time = 0;
+ nbg_time = 0;
+ enemydeath_time = 0;
+ teleport_time = 0;
+ flags = 0;
+ firethrottlewait_time = 0;
+ attackchase_time = 0;
+ attackcrouch_time = 0;
+ attackstrafe_time = 0;
+ attackjump_time = 0;
+ firethrottleshoot_time = 0;
+ chase_time = 0;
+ thinktime = 0;
+ useRandomPosition = false;
+ aimtarget.Zero();
+ lastenemyorigin.Zero();
+ origin.Zero();
+ enemyorigin.Zero();
+ random_move_position.Zero();
+ last_enemy_visible_position.Zero();
+ viewangles.Zero();
+ eye.Zero();
+ memset( &inventory[0], 0, sizeof( inventory ) );
+ }
+
+ bot_character_t* character;
+ int gs;
+ int ws;
+ int enemy;
+ int client;
+ idEntity* attackerEntity;
+ int lasthealth;
+ int entitynum;
+ int setupcount;
+ int ltg_time;
+ int flags;
+ int weaponnum;
+ bool useRandomPosition;
+ float thinktime;
+ float chase_time;
+ float attackjump_time;
+ float attackcrouch_time;
+ float attackstrafe_time;
+ float attackchase_time;
+ float firethrottlewait_time;
+ float firethrottleshoot_time;
+ float nbg_time; //nearby goal time
+ float entergame_time;
+ float weaponchange_time;
+ float check_time;
+ float teleport_time;
+ float enemyvisible_time; //time the enemy was last visible
+ int enemysuicide; //true when the enemy of the bot suicides
+ float enemysight_time; //time before reacting to enemy
+ float enemydeath_time; //time the enemy died
+ float aggressiveAttackTime;
+ idVec3 origin;
+ idVec3 aimtarget;
+ idVec3 random_move_position;
+ idVec3 last_enemy_visible_position;
+ idAngles viewangles;
+ idVec3 enemyorigin;
+ idVec3 eye;
+ idVec3 lastenemyorigin;
+ int inventory[MAX_BOT_INVENTORY];
+ bot_goal_t currentGoal;
+ bot_input_t botinput;
+};
+
+#include "Bot_chat.h"
+
+#define Bot_Time() ((float)gameLocal.time / 1000.0f)
+
+//
+// rvmBot
+//
+class rvmBot : public idPlayer
+{
+public:
+ friend class rvmBotAI;
+
+ CLASS_PROTOTYPE( rvmBot );
+
+ rvmBot();
+ ~rvmBot();
+
+ void Spawn( void );
+ virtual void Think( void ) override;
+ virtual void SpawnToPoint( const idVec3& spawn_origin, const idAngles& spawn_angles ) override;
+ virtual void Damage( idEntity* inflictor, idEntity* attacker, const idVec3& dir, const char* damageDefName, const float damageScale, const int location ) override;
+ virtual void InflictedDamageEvent(idEntity* target) override;
+ virtual void StateThreadChanged(void) override;
+
+ void SetEnemy( idPlayer* player, idVec3 origin );
+
+ void BotInputFrame( idUserCmdMgr& cmdMgr );
+ void Bot_ResetUcmd( usercmd_t& ucmd );
+
+ static void PresenceTypeBoundingBox( int presencetype, idVec3& mins, idVec3& maxs );
+private:
+ void BotSendChatMessage(botChat_t chat, const char *targetName);
+
+ void BotInputToUserCommand( bot_input_t* bi, usercmd_t* ucmd, int time );
+
+ void BotMoveToGoalOrigin( idVec3 goalOrigin );
+
+ void ServerThink( void );
+ void BotUpdateInventory( void );
+
+ bool HasWeapon( int index )
+ {
+ return inventory.weapons & ( 1 << index );
+ }
+private:
+ bot_state_t bs;
+ bool hasSpawned;
+
+private:
+ int weapon_machinegun;
+ int weapon_shotgun;
+ int weapon_plasmagun;
+ int weapon_rocketlauncher;
+protected:
+ bool BotIsDead(bot_state_t* bs);
+ bool BotReachedGoal(bot_state_t* bs, bot_goal_t* goal);
+ int BotGetItemLongTermGoal(bot_state_t* bs, int tfl, bot_goal_t* goal);
+ void BotChooseWeapon(bot_state_t* bs);
+ int BotFindEnemy(bot_state_t* bs, int curenemy);
+ bool EntityIsDead(idEntity* entity);
+ float BotEntityVisible(int viewer, idVec3 eye, idAngles viewangles, float fov, int ent);
+ float BotEntityVisibleTest(int viewer, idVec3 eye, idAngles viewangles, float fov, int ent, bool allowHeightTest);
+ void BotUpdateBattleInventory(bot_state_t* bs, int enemy);
+ float BotAggression(bot_state_t* bs);
+ int BotWantsToRetreat(bot_state_t* bs);
+ void BotBattleUseItems(bot_state_t* bs);
+ void BotAimAtEnemy(bot_state_t* bs);
+ void BotCheckAttack(bot_state_t* bs);
+ bool BotWantsToChase(bot_state_t* bs);
+ int BotNearbyGoal(bot_state_t* bs, int tfl, bot_goal_t* ltg, float range);
+ void BotGetRandomPointNearPosition(idVec3 point, idVec3& randomPoint, float radius);
+ int BotMoveInRandomDirection(bot_state_t* bs);
+ void BotMoveToGoal(bot_state_t* bs, bot_goal_t* goal);
+
+ void MoveToCoverPoint(void);
+
+ static int WP_MACHINEGUN;
+ static int WP_SHOTGUN;
+ static int WP_PLASMAGUN;
+ static int WP_ROCKET_LAUNCHER;
+private:
+ stateResult_t state_Chase(stateParms_t* parms);
+ stateResult_t state_BattleFight(stateParms_t* parms);
+ stateResult_t state_BattleNBG(stateParms_t* parms);
+ stateResult_t state_Retreat(stateParms_t* parms);
+ stateResult_t state_Respawn(stateParms_t* parms);
+ stateResult_t state_SeekNBG(stateParms_t* parms);
+ stateResult_t state_SeekLTG(stateParms_t* parms);
+ stateResult_t state_Attacked(stateParms_t* parms);
+private:
+ idAAS* aas;
+};
+
+extern idCVar bot_skill;
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI.cpp b/neo/doom3/game/bots/BotAI.cpp
new file mode 100644
index 00000000..c39396d5
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI.cpp
@@ -0,0 +1,1376 @@
+// BotAI.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+#include "../ai/AASCallback_FindCoverArea.h"
+
+#define IDEAL_ATTACKDIST 140
+
+int rvmBot::WP_MACHINEGUN = -1;
+int rvmBot::WP_SHOTGUN = -1;
+int rvmBot::WP_PLASMAGUN = -1;
+int rvmBot::WP_ROCKET_LAUNCHER = -1;
+
+/*
+=========================
+rvmBot::BotIsDead
+=========================
+*/
+bool rvmBot::BotIsDead( bot_state_t* bs )
+{
+ idPlayer* player = gameLocal.GetClientByNum( bs->client );
+ if( player->health <= 0 )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+==================
+rvmBot::BotReachedGoal
+==================
+*/
+bool rvmBot::BotReachedGoal( bot_state_t* bs, bot_goal_t* goal )
+{
+ if( goal->flags & GFL_ITEM )
+ {
+ //if touching the goal
+ if( botGoalManager.BotTouchingGoal( bs->origin, goal ) )
+ {
+ if( !( goal->flags & GFL_DROPPED ) )
+ {
+ botGoalManager.BotSetAvoidGoalTime( bs->gs, goal->number, -1 );
+ }
+ return true;
+ }
+ //if the goal isn't there
+ if( botGoalManager.BotItemGoalInVisButNotVisible( bs->entitynum, bs->eye, bs->viewangles, goal ) )
+ {
+ return true;
+ }
+ }
+ else
+ {
+ //if touching the goal
+ if( botGoalManager.BotTouchingGoal( bs->origin, goal ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+==================
+rvmBot::BotChooseWeapon
+==================
+*/
+void rvmBot::BotChooseWeapon( bot_state_t* bs )
+{
+ int newweaponnum;
+
+ //if (bs->cur_ps.weaponstate == WEAPON_RAISING ||
+ // bs->cur_ps.weaponstate == WEAPON_DROPPING) {
+ // //trap_EA_SelectWeapon(bs->client, bs->weaponnum);
+ // bs->input.weapon = bs->weaponnum;
+ //}
+ //else {
+ newweaponnum = botWeaponInfoManager.BotChooseBestFightWeapon( bs->ws, bs->inventory );
+ if( bs->weaponnum != newweaponnum )
+ {
+ bs->weaponchange_time = Bot_Time();
+ }
+ bs->weaponnum = newweaponnum;
+ //BotAI_Print(PRT_MESSAGE, "bs->weaponnum = %d\n", bs->weaponnum);
+ //trap_EA_SelectWeapon(bs->client, bs->weaponnum);
+ bs->botinput.weapon = bs->weaponnum;
+ //}
+}
+
+
+/*
+==================
+rvmBot::BotGetItemLongTermGoal
+==================
+*/
+int rvmBot::BotGetItemLongTermGoal( bot_state_t* bs, int tfl, bot_goal_t* goal )
+{
+ //if the bot has no goal
+ if( !botGoalManager.BotGetTopGoal( bs->gs, goal ) )
+ {
+ //BotAI_Print(PRT_MESSAGE, "no ltg on stack\n");
+ bs->ltg_time = 0;
+ }
+ //if the bot touches the current goal
+ else if( BotReachedGoal( bs, goal ) )
+ {
+ BotChooseWeapon( bs );
+ bs->ltg_time = 0;
+ }
+
+ // Check to see that we can get to our goal, if not get a new goal.
+ //if (bs->numMovementWaypoints > 0)
+ //{
+ // trace_t tr;
+ // gentity_t* ent = &g_entities[bs->client];
+ // vec3_t waypoint;
+ //
+ // VectorCopy(bs->movement_waypoints[bs->currentWaypoint], waypoint);
+ // waypoint[2] += 5.0f;
+ //
+ // trap_Trace(&tr, ent->r.currentOrigin, NULL, NULL, waypoint, bs->client, CONTENTS_SOLID);
+ //
+ // if (tr.fraction <= 0.7f)
+ // {
+ // BotChooseWeapon(bs);
+ // bs->ltg_time = 0;
+ // }
+ //}
+
+ //if it is time to find a new long term goal
+ if( bs->ltg_time == 0 )
+ {
+ //pop the current goal from the stack
+ botGoalManager.BotPopGoal( bs->gs );
+ //BotAI_Print(PRT_MESSAGE, "%s: choosing new ltg\n", ClientName(bs->client, netname, sizeof(netname)));
+ //choose a new goal
+ //BotAI_Print(PRT_MESSAGE, "%6.1f client %d: BotChooseLTGItem\n", Bot_Time(), bs->client);
+ if( botGoalManager.BotChooseLTGItem( bs->gs, bs->origin, bs->inventory, tfl ) )
+ {
+ char buf[128];
+ //get the goal at the top of the stack
+ botGoalManager.BotGetTopGoal( bs->gs, goal );
+ botGoalManager.BotGoalName( goal->number, buf, sizeof( buf ) );
+ common->Printf( "%1.1f: new long term goal %s\n", Bot_Time(), buf );
+
+ bs->ltg_time = Bot_Time() + 20;
+ bs->currentGoal.framenum = gameLocal.framenum;
+ }
+ else //the bot gets sorta stuck with all the avoid timings, shouldn't happen though
+ {
+ //
+ //trap_BotDumpAvoidGoals(bs->gs);
+ //reset the avoid goals and the avoid reach
+ botGoalManager.BotResetAvoidGoals( bs->gs );
+ //BotResetAvoidReach(bs->ms);
+ }
+ //get the goal at the top of the stack
+ if( !botGoalManager.BotGetTopGoal( bs->gs, goal ) )
+ {
+ return false;
+ }
+
+ bs->currentGoal.framenum = gameLocal.framenum;
+
+ return true;
+ }
+ return true;
+}
+
+/*
+==================
+rvmBot::EntityIsDead
+==================
+*/
+bool rvmBot::EntityIsDead( idEntity* entity )
+{
+ {
+ idPlayer* player = entity->Cast();
+ if( player && player->health <= 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+==================
+BotEntityVisibleTest
+
+returns visibility in the range [0, 1] taking fog and water surfaces into account
+==================
+*/
+float rvmBot::BotEntityVisibleTest( int viewer, idVec3 eye, idAngles viewangles, float fov, int ent, bool allowHeightTest )
+{
+ int i, contents_mask, passent, hitent, infog, inwater, otherinfog, pc;
+ float squaredfogdist, waterfactor, vis, bestvis;
+ trace_t trace;
+ idEntity* entinfo;
+ idVec3 dir, start, end, middle;
+ idAngles entangles;
+ idPlayer* viewEnt;
+
+ //calculate middle of bounding box
+ //BotEntityInfo(ent, &entinfo);
+ entinfo = gameLocal.entities[ent];
+ viewEnt = gameLocal.entities[viewer]->Cast();
+
+ //VectorAdd(entinfo->r.mins, entinfo->r.maxs, middle);
+ //VectorScale(middle, 0.5, middle);
+ //VectorAdd(entinfo->GetOrigin(), middle, middle);
+ middle = entinfo->GetPhysics()->GetBounds().GetCenter();
+ middle += entinfo->GetOrigin();
+
+ //check if entity is within field of vision
+ //VectorSubtract(middle, eye, dir);
+ dir = middle - eye;
+ entangles = dir.ToAngles();
+
+ if (!viewEnt->CheckFOV(entinfo->GetOrigin()))
+ {
+ return 0;
+ }
+
+ pc = gameLocal.clip.PointContents( eye );
+ infog = ( pc & CONTENTS_FOG );
+ inwater = ( pc & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) );
+ bestvis = 0;
+ for( i = 0; i < 3; i++ )
+ {
+ //if the point is not in potential visible sight
+ //if (!AAS_inPVS(eye, middle)) continue;
+ //
+ contents_mask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
+ passent = viewer;
+ hitent = ent;
+ //VectorCopy(eye, start);
+ start = eye;
+ //VectorCopy(middle, end);
+ end = middle;
+ //if the entity is in water, lava or slime
+ if( gameLocal.clip.PointContents( middle ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) )
+ {
+ contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
+ }
+ //if eye is in water, lava or slime
+ if( inwater )
+ {
+ if( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) )
+ {
+ passent = ent;
+ hitent = viewer;
+ VectorCopy( middle, start );
+ VectorCopy( eye, end );
+ }
+ contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
+ }
+
+ //trace from start to end
+ gameLocal.Trace( trace, start, end, contents_mask, passent );
+ // jmarshall
+ if( trace.fraction < 0.9f && allowHeightTest )
+ {
+ end[2] += 50.0f;
+ gameLocal.Trace( trace, start, end, contents_mask, passent );
+ }
+ // jmarshall end
+
+ //if water was hit
+ waterfactor = 1.0;
+ if( trace.c.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) )
+ {
+ //if the water surface is translucent
+ if( 1 )
+ {
+ //trace through the water
+ contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
+ //trap_Trace(&trace, trace.endpos, NULL, NULL, end, passent, contents_mask);
+ gameLocal.Trace( trace, trace.endpos, end, contents_mask, passent );
+ waterfactor = 0.5;
+ }
+ }
+
+ //if a full trace or the hitent was hit
+ if( trace.fraction >= 1 || trace.c.entityNum == hitent )
+ {
+ //check for fog, assuming there's only one fog brush where
+ //either the viewer or the entity is in or both are in
+ otherinfog = ( gameLocal.clip.PointContents( middle ) & CONTENTS_FOG );
+ if( infog && otherinfog )
+ {
+ VectorSubtract( trace.endpos, eye, dir );
+ squaredfogdist = dir.LengthSqr();
+ }
+ else if( infog )
+ {
+ VectorCopy( trace.endpos, start );
+ //trap_Trace(&trace, start, NULL, NULL, eye, viewer, CONTENTS_FOG);
+ gameLocal.Trace( trace, start, eye, CONTENTS_FOG, viewer );
+ VectorSubtract( eye, trace.endpos, dir );
+ squaredfogdist = dir.LengthSqr();
+ }
+ else if( otherinfog )
+ {
+ VectorCopy( trace.endpos, end );
+ //trap_Trace(&trace, eye, NULL, NULL, end, viewer, CONTENTS_FOG);
+ gameLocal.Trace( trace, eye, end, CONTENTS_FOG, viewer );
+ VectorSubtract( end, trace.endpos, dir );
+ squaredfogdist = dir.LengthSqr();
+ }
+ else
+ {
+ //if the entity and the viewer are not in fog assume there's no fog in between
+ squaredfogdist = 0;
+ }
+ //decrease visibility with the view distance through fog
+ vis = 1 / ( ( squaredfogdist * 0.001 ) < 1 ? 1 : ( squaredfogdist * 0.001 ) );
+
+ //if entering water visibility is reduced
+ vis *= waterfactor;
+
+ if( vis > bestvis )
+ {
+ bestvis = vis;
+ }
+
+ //if pretty much no fog
+ if( bestvis >= 0.95 )
+ {
+ return bestvis;
+ }
+ }
+ //check bottom and top of bounding box as well
+ if( i == 0 )
+ {
+ middle[2] += entinfo->GetPhysics()->GetBounds()[0][2]; // r.mins[2];
+ }
+ else if( i == 1 )
+ {
+ middle[2] += entinfo->GetPhysics()->GetBounds()[1][2] - entinfo->GetPhysics()->GetBounds()[0][2]; //entinfo->r.maxs[2] - entinfo->r.mins[2];
+ }
+ }
+ return bestvis;
+}
+
+/*
+==================
+rvmBot::BotEntityVisible
+==================
+*/
+float rvmBot::BotEntityVisible( int viewer, idVec3 eye, idAngles viewangles, float fov, int ent )
+{
+ return BotEntityVisibleTest( viewer, eye, viewangles, fov, ent, true );
+}
+
+/*
+==================
+rvmBot::BotUpdateBattleInventory
+==================
+*/
+void rvmBot::BotUpdateBattleInventory( bot_state_t* bs, int enemy )
+{
+ idVec3 dir;
+ idEntity* entinfo;
+
+ entinfo = gameLocal.entities[enemy];
+
+ VectorSubtract( entinfo->GetOrigin(), bs->origin, dir );
+ bs->inventory[ENEMY_HEIGHT] = ( int )dir[2];
+ dir[2] = 0;
+ bs->inventory[ENEMY_HORIZONTAL_DIST] = ( int )dir.Length();
+ //FIXME: add num visible enemies and num visible team mates to the inventory
+}
+
+/*
+==================
+rvmBot::BotAggression
+==================
+*/
+float rvmBot::BotAggression( bot_state_t* bs )
+{
+ //if the bot has quad
+ if( bs->inventory[INVENTORY_QUAD] )
+ {
+ //if the bot is not holding the gauntlet or the enemy is really nearby
+ if( bs->weaponnum != 0 ||
+ bs->inventory[ENEMY_HORIZONTAL_DIST] < 80 )
+ {
+ return 70;
+ }
+ }
+ //if the enemy is located way higher than the bot
+ if( bs->inventory[ENEMY_HEIGHT] > 200 )
+ {
+ return 0;
+ }
+ //if the bot is very low on health
+ if( bs->inventory[INVENTORY_HEALTH] < 60 )
+ {
+ return 0;
+ }
+ //if the bot is low on health
+ if( bs->inventory[INVENTORY_HEALTH] < 80 )
+ {
+ //if the bot has insufficient armor
+ if( bs->inventory[INVENTORY_ARMOR] < 40 )
+ {
+ return 0;
+ }
+ }
+ //if the bot can use the bfg
+ if( bs->inventory[INVENTORY_BFG10K] > 0 &&
+ bs->inventory[INVENTORY_BFGAMMO] > 7 )
+ {
+ return 100;
+ }
+ //if the bot can use the railgun
+ if( bs->inventory[INVENTORY_RAILGUN] > 0 &&
+ bs->inventory[INVENTORY_SLUGS] > 5 )
+ {
+ return 95;
+ }
+ //if the bot can use the lightning gun
+ if( bs->inventory[INVENTORY_LIGHTNING] > 0 &&
+ bs->inventory[INVENTORY_LIGHTNINGAMMO] > 50 )
+ {
+ return 90;
+ }
+ //if the bot can use the rocketlauncher
+ if( bs->inventory[INVENTORY_ROCKETLAUNCHER] > 0 &&
+ bs->inventory[INVENTORY_ROCKETS] > 5 )
+ {
+ return 90;
+ }
+ //if the bot can use the plasmagun
+ if( bs->inventory[INVENTORY_PLASMAGUN] > 0 &&
+ bs->inventory[INVENTORY_CELLS] > 40 )
+ {
+ return 85;
+ }
+ //if the bot can use the grenade launcher
+ if( bs->inventory[INVENTORY_GRENADELAUNCHER] > 0 &&
+ bs->inventory[INVENTORY_GRENADES] > 10 )
+ {
+ return 80;
+ }
+ //if the bot can use the shotgun
+ if( bs->inventory[INVENTORY_SHOTGUN] > 0 &&
+ bs->inventory[INVENTORY_SHELLS] > 10 )
+ {
+ return 50;
+ }
+
+ if( bs->inventory[INVENTORY_BULLETS] > 0 )
+ {
+ return 60;
+ }
+
+ //otherwise the bot is not feeling too good
+ return 0;
+}
+
+
+/*
+==================
+rvmBot::BotWantsToRetreat
+==================
+*/
+int rvmBot::BotWantsToRetreat( bot_state_t* bs )
+{
+ if( BotAggression( bs ) < 50 )
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+==================
+rvmBot::BotBattleUseItems
+==================
+*/
+void rvmBot::BotBattleUseItems( bot_state_t* bs )
+{
+ if( bs->inventory[INVENTORY_HEALTH] < 40 )
+ {
+ if( bs->inventory[INVENTORY_TELEPORTER] > 0 )
+ {
+ bs->botinput.actionflags |= ACTION_USE;
+ }
+ }
+ if( bs->inventory[INVENTORY_HEALTH] < 60 )
+ {
+ if( bs->inventory[INVENTORY_MEDKIT] > 0 )
+ {
+ //trap_EA_Use(bs->client);
+ bs->botinput.actionflags |= ACTION_USE;
+ }
+ }
+}
+
+/*
+==================
+rvmBot::BotFindEnemy
+==================
+*/
+int rvmBot::BotFindEnemy( bot_state_t* bs, int curenemy )
+{
+ int i, healthdecrease;
+ float f, alertness, easyfragger, vis;
+ float squaredist, cursquaredist;
+ idPlayer* entinfo = NULL;
+ idPlayer* curenemyinfo = NULL;
+ idVec3 dir;
+ idAngles angles;
+ idPlayer* clientEnt;
+
+ alertness = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_ALERTNESS, 0, 1 );
+ easyfragger = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1 );
+
+ clientEnt = gameLocal.entities[bs->client]->Cast();
+
+ //check if the health decreased
+ healthdecrease = bs->lasthealth > bs->inventory[INVENTORY_HEALTH];
+
+ //remember the current health value
+ bs->lasthealth = bs->inventory[INVENTORY_HEALTH];
+ //
+ if( curenemy >= 0 )
+ {
+ //BotEntityInfo(curenemy, &curenemyinfo);
+ curenemyinfo = gameLocal.entities[curenemy]->Cast();
+ // jmarshall - add flag support.
+ //if (EntityCarriesFlag(&curenemyinfo)) return qfalse;
+ // jmarshall end
+ //VectorSubtract(curenemyinfo->r.currentOrigin, bs->origin, dir);
+ dir = curenemyinfo->GetPhysics()->GetOrigin() - bs->origin;
+ cursquaredist = dir.LengthSqr();// VectorLengthSquared(dir);
+ }
+ else
+ {
+ cursquaredist = 0;
+ }
+
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+
+ if( i == bs->client )
+ {
+ continue;
+ }
+
+ //if it's the current enemy
+ if( i == curenemy )
+ {
+ continue;
+ }
+
+ entinfo = gameLocal.entities[i]->Cast();
+ if( !entinfo )
+ {
+ continue;
+ }
+
+ //if the enemy isn't dead and the enemy isn't the bot self
+ if( EntityIsDead( entinfo ) || i == bs->entitynum )
+ {
+ continue;
+ }
+
+ if (entinfo->spectating) {
+ continue;
+ }
+
+ //if the enemy is invisible and not shooting
+// jmarshall - add invis
+ //if (EntityIsInvisible(&entinfo) && !EntityIsShooting(&entinfo)) {
+ // continue;
+ //}
+// jmarshall end
+
+// jmarshall - eval, looks like code to not shoot chatting players or players that just spawned in.
+// do we care about this?
+ //if not an easy fragger don't shoot at chatting players
+ //if (easyfragger < 0.5 && EntityIsChatting(&entinfo))
+ // continue;
+
+ //
+ //if (lastteleport_time > Bot_Time() - 3) {
+ // VectorSubtract(entinfo.origin, lastteleport_origin, dir);
+ // if (VectorLengthSquared(dir) < Square(70))
+ // continue;
+ //}
+// jmarshall end
+
+ //calculate the distance towards the enemy
+ idVec3 potentialTargetOrigin = entinfo->GetPhysics()->GetOrigin();
+ dir = potentialTargetOrigin - bs->origin;
+ squaredist = dir.LengthSqr();
+
+ // jmarshall
+ //if this entity is not carrying a flag
+ //if (!EntityCarriesFlag(&entinfo))
+ //{
+ //if this enemy is further away than the current one
+ if( curenemy >= 0 && squaredist > cursquaredist )
+ {
+ continue;
+ }
+ //}
+// jmarshall end
+
+ //if the bot has no
+ if( squaredist > Square( 900.0 + alertness * 4000.0 ) )
+ {
+ continue;
+ }
+
+ // jmarshall - teams!
+ //if on the same team
+ //if (BotSameTeam(bs, i))
+ // continue;
+ // jmarshall end
+ //if the bot's health decreased or the enemy is shooting
+ if( curenemy < 0 && ( healthdecrease || entinfo->IsShooting() ) )
+ {
+ f = 360;
+ }
+ else
+ {
+ f = 90 + 90 - ( 90 - ( squaredist > Square( 810 ) ? Square( 810 ) : squaredist ) / ( 810 * 9 ) );
+ }
+ //check if the enemy is visible
+
+ // If we were last hit by someone then assume they are visible.
+ if( bs->attackerEntity != entinfo )
+ {
+ vis = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, f, i );
+ if( vis <= 0 )
+ {
+ continue;
+ }
+ }
+
+ //if the enemy is quite far away, not shooting and the bot is not damaged
+ if( curenemy < 0 && squaredist > Square( 100 ) && !healthdecrease && !entinfo->IsShooting() )
+ {
+ //check if we can avoid this enemy
+ VectorSubtract( bs->origin, entinfo->GetOrigin(), dir );
+ angles = dir.ToAngles();
+
+ //if the bot isn't in the fov of the enemy
+ if( !clientEnt->CheckFOV( entinfo->GetOrigin() ) )
+ {
+ //update some stuff for this enemy
+ BotUpdateBattleInventory( bs, i );
+
+ //if the bot doesn't really want to fight
+ if( BotWantsToRetreat( bs ) )
+ {
+ continue;
+ }
+ }
+ }
+ //found an enemy
+ bs->enemy = i;//entinfo.number;
+ if( curenemy >= 0 )
+ {
+ bs->enemysight_time = Bot_Time() - 2;
+ }
+ else
+ {
+ bs->enemysight_time = Bot_Time();
+ }
+ bs->enemysuicide = false;
+ bs->enemydeath_time = 0;
+ bs->enemyvisible_time = Bot_Time();
+ return true;
+ }
+ return false;
+}
+
+/*
+==================
+rvmBot::BotMoveToGoal
+==================
+*/
+void rvmBot::BotMoveToGoal( bot_state_t* bs, bot_goal_t* goal )
+{
+ bs->currentGoal = *goal;
+ bs->currentGoal.framenum = gameLocal.framenum;
+}
+
+/*
+==================
+rvmBot::BotAimAtEnemy
+==================
+*/
+void rvmBot::BotAimAtEnemy( bot_state_t* bs )
+{
+ int i, enemyvisible;
+ float dist, f, aim_skill, aim_accuracy, speed, reactiontime;
+ idVec3 dir, bestorigin, end, start, groundtarget, cmdmove, enemyvelocity;
+ idVec3 mins( -4, -4, -4 );
+ idVec3 maxs( 4, 4, 4 );
+ weaponinfo_t wi;
+ //aas_entityinfo_t entinfo;
+ idPlayer* entinfo;
+ bot_goal_t goal;
+ trace_t trace;
+ idVec3 target;
+ idPlayer* self;
+
+ //if the bot has no enemy
+ if( bs->enemy < 0 )
+ {
+ return;
+ }
+
+ self = gameLocal.entities[bs->entitynum]->Cast();
+
+ //get the enemy entity information
+ //BotEntityInfo(bs->enemy, &entinfo);
+ entinfo = gameLocal.entities[bs->enemy]->Cast();
+
+ //if this is not a player (should be an obelisk)
+ if( bs->enemy >= MAX_CLIENTS )
+ {
+ //if the obelisk is visible
+ VectorCopy( entinfo->GetOrigin(), target );
+#ifdef MISSIONPACK
+ // if attacking an obelisk
+ if( bs->enemy == redobelisk.entitynum ||
+ bs->enemy == blueobelisk.entitynum )
+ {
+ target[2] += 32;
+ }
+#endif
+ //aim at the obelisk
+ VectorSubtract( target, bs->eye, dir );
+ //vectoangles(dir, bs->viewangles);
+ bs->viewangles = dir.ToAngles();
+
+ //set the aim target before trying to attack
+ VectorCopy( target, bs->aimtarget );
+ return;
+ }
+
+ //
+ //BotAI_Print(PRT_MESSAGE, "client %d: aiming at client %d\n", bs->entitynum, bs->enemy);
+ //
+ aim_skill = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL, 0, 1 );
+ aim_accuracy = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY, 0, 1 );
+ //
+ if( aim_skill > 0.95 )
+ {
+ //don't aim too early
+ reactiontime = 0.5 * botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1 );
+ if( bs->enemysight_time > Bot_Time() - reactiontime )
+ {
+ return;
+ }
+ if( bs->teleport_time > Bot_Time() - reactiontime )
+ {
+ return;
+ }
+ }
+
+ //get the weapon information
+ botWeaponInfoManager.BotGetWeaponInfo( bs->ws, bs->weaponnum, &wi );
+
+ //get the weapon specific aim accuracy and or aim skill
+ if( wi.number == WP_MACHINEGUN )
+ {
+ aim_accuracy = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN, 0, 1 );
+ }
+ else if( wi.number == WP_SHOTGUN )
+ {
+ aim_accuracy = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_SHOTGUN, 0, 1 );
+ }
+ //else if (wi.number == WP_GRENADE_LAUNCHER) {
+ // aim_accuracy = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER, 0, 1);
+ // aim_skill = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER, 0, 1);
+ //}
+ else if( wi.number == WP_ROCKET_LAUNCHER )
+ {
+ aim_accuracy = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER, 0, 1 );
+ aim_skill = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER, 0, 1 );
+ }
+ //else if (wi.number == WP_LIGHTNING) {
+ // aim_accuracy = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_LIGHTNING, 0, 1);
+ //}
+ //else if (wi.number == WP_RAILGUN) {
+ // aim_accuracy = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_RAILGUN, 0, 1);
+ //}
+ else if( wi.number == WP_PLASMAGUN )
+ {
+ aim_accuracy = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN, 0, 1 );
+ aim_skill = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_AIM_SKILL_PLASMAGUN, 0, 1 );
+ }
+ //else if (wi.number == WP_BFG) {
+ // aim_accuracy = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_ACCURACY_BFG10K, 0, 1);
+ // aim_skill = Characteristic_BFloat(bs->character, CHARACTERISTIC_AIM_SKILL_BFG10K, 0, 1);
+ //}
+ //
+ if( aim_accuracy <= 0 )
+ {
+ aim_accuracy = 0.0001f;
+ }
+ //get the enemy entity information
+ //BotEntityInfo(bs->enemy, &entinfo);
+ entinfo = gameLocal.entities[bs->enemy]->Cast();
+
+ //if the enemy is invisible then shoot crappy most of the time
+ //if (entinfo->IsInvisible()) {
+ // if (rvmBotUtil::random() > 0.1)
+ // aim_accuracy *= 0.4f;
+ //}
+ // jmarshall - fix aim accuracy.
+ //VectorSubtract(entinfo->GetOrigin(), entinfo.lastvisorigin, enemyvelocity);
+ //VectorScale(enemyvelocity, 1 / entinfo.update_time, enemyvelocity);
+ ////enemy origin and velocity is remembered every 0.5 seconds
+ //if (bs->enemyposition_time < Bot_Time()) {
+ // //
+ // bs->enemyposition_time = Bot_Time() + 0.5;
+ // VectorCopy(enemyvelocity, bs->enemyvelocity);
+ // VectorCopy(entinfo.origin, bs->enemyorigin);
+ //}
+ ////if not extremely skilled
+ //if (aim_skill < 0.9) {
+ // VectorSubtract(entinfo.origin, bs->enemyorigin, dir);
+ // //if the enemy moved a bit
+ // if (VectorLengthSquared(dir) > Square(48)) {
+ // //if the enemy changed direction
+ // if (DotProduct(bs->enemyvelocity, enemyvelocity) < 0) {
+ // //aim accuracy should be worse now
+ // aim_accuracy *= 0.7f;
+ // }
+ // }
+ //}
+ // jmarshall end
+
+ //check visibility of enemy
+ enemyvisible = BotEntityVisible( bs->entitynum, bs->eye, bs->viewangles, 360, bs->enemy );
+
+ //if the enemy is visible
+ if( enemyvisible )
+ {
+ //
+ VectorCopy( entinfo->GetOrigin(), bestorigin );
+ bestorigin[2] += 8;
+ //get the start point shooting from
+ //NOTE: the x and y projectile start offsets are ignored
+ VectorCopy( bs->origin, start );
+ start[2] += self->GetViewHeight();// bs->cur_ps.viewheight;
+ start[2] += wi.offset[2];
+ //
+ //trap_Trace(&trace, start, mins, maxs, bestorigin, bs->entitynum, MASK_SHOT);
+ gameLocal.Trace( trace, start, bestorigin, MASK_SHOT, bs->entitynum );
+ //if the enemy is NOT hit
+ if( trace.fraction <= 1 && trace.c.entityNum != bs->enemy )
+ {
+ bestorigin[2] += 16;
+ }
+ //if it is not an instant hit weapon the bot might want to predict the enemy
+ if( wi.speed )
+ {
+ //
+ VectorSubtract( bestorigin, bs->origin, dir );
+ dist = dir.Length();
+ VectorSubtract( entinfo->GetOrigin(), bs->enemyorigin, dir );
+ //if the enemy is NOT pretty far away and strafing just small steps left and right
+ if( !( dist > 100 && dir.LengthSqr() < Square( 32 ) ) )
+ {
+ //if skilled anough do exact prediction
+ if( aim_skill > 0.8 &&
+ //if the weapon is ready to fire
+ !self->IsShooting() )
+ {
+ //aas_clientmove_t move;
+ idVec3 origin;
+ idVec3 last_enemy_visible_position;
+ VectorCopy( last_enemy_visible_position, bs->last_enemy_visible_position );
+ last_enemy_visible_position[2] += 20.0f;
+
+ //
+ VectorSubtract( entinfo->GetOrigin(), bs->origin, dir );
+
+ ////distance towards the enemy
+ //dist = VectorLength(dir);
+ ////direction the enemy is moving in
+ VectorSubtract( entinfo->GetOrigin(), last_enemy_visible_position, dir );
+ ////
+ //VectorScale(dir, 1 / entinfo->update_time, dir);
+ ////
+ //VectorCopy(entinfo->origin, origin);
+ //origin[2] += 1;
+
+ ////
+ //VectorClear(cmdmove);
+ ////AAS_ClearShownDebugLines();
+ //trap_AAS_PredictClientMovement(&move, bs->enemy, origin,
+ // PRESENCE_CROUCH, qfalse,
+ // dir, cmdmove, 0,
+ // dist * 10 / wi.speed, 0.1f, 0, 0, qfalse);
+ //VectorCopy(move.endpos, bestorigin);
+ //BotAI_Print(PRT_MESSAGE, "%1.1f predicted speed = %f, frames = %f\n", Bot_Time(), VectorLength(dir), dist * 10 / wi.speed);
+
+ bot_goal_t goal;
+ VectorMA( entinfo->GetOrigin(), 30, dir, goal.origin );
+ BotMoveToGoal( bs, &goal );
+ }
+ //if not that skilled do linear prediction
+ else if( aim_skill > 0.4 )
+ {
+ // jmarshall - fix linear prediction.
+ idVec3 last_enemy_visible_position;
+ VectorCopy( last_enemy_visible_position, bs->last_enemy_visible_position );
+ last_enemy_visible_position[2] += 20.0f;
+
+ //VectorSubtract(entinfo->GetOrigin(), bs->origin, dir);
+ ////distance towards the enemy
+ //dist = VectorLength(dir);
+ ////direction the enemy is moving in
+ VectorSubtract( entinfo->GetOrigin(), last_enemy_visible_position, dir );
+ //dir[2] = 0;
+ ////
+ //speed = VectorNormalize(dir) / entinfo.update_time;
+ ////botimport.Print(PRT_MESSAGE, "speed = %f, wi->speed = %f\n", speed, wi->speed);
+ ////best spot to aim at
+ //VectorMA(entinfo.origin, (dist / wi.speed) * speed, dir, bestorigin);
+ bot_goal_t goal;
+ VectorMA( entinfo->GetOrigin(), 30, dir, goal.origin );
+ BotMoveToGoal( bs, &goal );
+ // jmarshall end
+ }
+ }
+ }
+ //if the projectile does radial damage
+ if( aim_skill > 0.6 && wi.proj.damagetype & DAMAGETYPE_RADIAL )
+ {
+ //if the enemy isn't standing significantly higher than the bot
+ if( entinfo->GetOrigin()[2] < bs->origin[2] + 16 )
+ {
+ //try to aim at the ground in front of the enemy
+ VectorCopy( entinfo->GetOrigin(), end );
+ end[2] -= 64;
+ //trap_Trace(&trace, entinfo->GetOrigin(), NULL, NULL, end, bs->enemy, MASK_SHOT);
+ gameLocal.Trace( trace, entinfo->GetOrigin(), end, MASK_SHOT, bs->enemy );
+ //
+ VectorCopy( bestorigin, groundtarget );
+// jmarshall - add start solid
+ //if (trace.startsolid)
+ // groundtarget[2] = entinfo->GetOrigin()[2] - 16;
+ //else
+ groundtarget[2] = trace.endpos[2] - 8;
+// jmarshall end
+ //trace a line from projectile start to ground target
+ //trap_Trace(&trace, start, NULL, NULL, groundtarget, bs->entitynum, MASK_SHOT);
+ gameLocal.Trace( trace, start, groundtarget, MASK_SHOT, bs->entitynum );
+ //if hitpoint is not vertically too far from the ground target
+ if( idMath::Fabs( trace.endpos[2] - groundtarget[2] ) < 50 )
+ {
+ VectorSubtract( trace.endpos, groundtarget, dir );
+ //if the hitpoint is near anough the ground target
+ if( dir.LengthSqr() < Square( 60 ) )
+ {
+ VectorSubtract( trace.endpos, start, dir );
+ //if the hitpoint is far anough from the bot
+ if( dir.LengthSqr() > Square( 100 ) )
+ {
+ //check if the bot is visible from the ground target
+ trace.endpos[2] += 1;
+ //trap_Trace(&trace, trace.endpos, NULL, NULL, entinfo->GetOrigin(), bs->enemy, MASK_SHOT);
+ gameLocal.Trace( trace, trace.endpos, entinfo->GetOrigin(), MASK_SHOT, bs->enemy );
+ if( trace.fraction >= 1 )
+ {
+ //botimport.Print(PRT_MESSAGE, "%1.1f aiming at ground\n", AAS_Time());
+ VectorCopy( groundtarget, bestorigin );
+ }
+ }
+ }
+ }
+ }
+ }
+ bestorigin[0] += 20 * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ bestorigin[1] += 20 * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ bestorigin[2] += 10 * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ }
+ else
+ {
+ //
+ VectorCopy( bs->lastenemyorigin, bestorigin );
+ bestorigin[2] += 8;
+ // jmarshall - fix this.
+ //if the bot is skilled anough
+ //if (aim_skill > 0.5) {
+ // //do prediction shots around corners
+ // if (wi.number == WP_BFG ||
+ // wi.number == WP_ROCKET_LAUNCHER ||
+ // wi.number == WP_GRENADE_LAUNCHER) {
+ // //create the chase goal
+ // goal.entitynum = bs->client;
+ // goal.areanum = bs->areanum;
+ // VectorCopy(bs->eye, goal.origin);
+ // VectorSet(goal.mins, -8, -8, -8);
+ // VectorSet(goal.maxs, 8, 8, 8);
+ // //
+ // if (trap_BotPredictVisiblePosition(bs->lastenemyorigin, bs->lastenemyareanum, &goal, TFL_DEFAULT, target)) {
+ // VectorSubtract(target, bs->eye, dir);
+ // if (VectorLengthSquared(dir) > Square(80)) {
+ // VectorCopy(target, bestorigin);
+ // bestorigin[2] -= 20;
+ // }
+ // }
+ // aim_accuracy = 1;
+ // }
+ //}
+ // jmarshall end
+ }
+ //
+ if( enemyvisible )
+ {
+ //trap_Trace(&trace, bs->eye, NULL, NULL, bestorigin, bs->entitynum, MASK_SHOT);
+ gameLocal.Trace( trace, bs->eye, bestorigin, MASK_SHOT, bs->entitynum );
+ VectorCopy( trace.endpos, bs->aimtarget );
+ }
+ else
+ {
+ VectorCopy( bestorigin, bs->aimtarget );
+ }
+ //get aim direction
+ VectorSubtract( bestorigin, bs->eye, dir );
+ //
+ if( wi.number == WP_MACHINEGUN ||
+ wi.number == WP_SHOTGUN /*|| // jmarshall add lighting railgun.
+ wi.number == WP_LIGHTNING ||
+ wi.number == WP_RAILGUN*/ )
+ {
+ //distance towards the enemy
+ dist = dir.Length();// VectorLength(dir);
+ if( dist > 150 )
+ {
+ dist = 150;
+ }
+ f = 0.6 + dist / 150 * 0.4;
+ aim_accuracy *= f;
+ }
+ //add some random stuff to the aim direction depending on the aim accuracy
+ if( aim_accuracy < 0.8 )
+ {
+ dir.Normalize();
+ for( i = 0; i < 3; i++ )
+ {
+ dir[i] += 0.3 * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ }
+ }
+ //set the ideal view angles
+ //vectoangles(dir, bs->viewangles);
+ bs->viewangles = dir.ToAngles();
+
+ //take the weapon spread into account for lower skilled bots
+ bs->viewangles[PITCH] += 6 * wi.vspread * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ bs->viewangles[PITCH] = idMath::AngleMod( bs->viewangles[PITCH] );
+ bs->viewangles[YAW] += 6 * wi.hspread * rvmBotUtil::crandom() * ( 1 - aim_accuracy );
+ bs->viewangles[YAW] = idMath::AngleMod( bs->viewangles[YAW] );
+ // jmarshall - add bot_challenge.
+ //if the bots should be really challenging
+ //if (bot_challenge.integer) {
+ // //if the bot is really accurate and has the enemy in view for some time
+ // if (aim_accuracy > 0.9 && bs->enemysight_time < Bot_Time() - 1) {
+ // //set the view angles directly
+ // if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360;
+ // VectorCopy(bs->ideal_viewangles, bs->viewangles);
+ // trap_EA_View(bs->client, bs->viewangles);
+ // }
+ //}
+ //vectoangles(bi->dir, bi->viewangles);
+ // jmarshall end
+}
+
+
+/*
+==================
+rvmBot::BotWantsToChase
+==================
+*/
+bool rvmBot::BotWantsToChase( bot_state_t* bs )
+{
+ if( BotAggression( bs ) > 50 )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+/*
+==================
+rvmBot::BotNearbyGoal
+==================
+*/
+int rvmBot::BotNearbyGoal( bot_state_t* bs, int tfl, bot_goal_t* ltg, float range )
+{
+ int ret;
+
+ // jmarshall - check for air.
+ //check if the bot should go for air
+ //if (BotGoForAir(bs, tfl, ltg, range)) return qtrue;
+ ////if the bot is carrying the enemy flag
+ //if (BotCTFCarryingFlag(bs)) {
+ // //if the bot is just a few secs away from the base
+ // if (trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin,
+ // bs->teamgoal.areanum, TFL_DEFAULT) < 300) {
+ // //make the range really small
+ // range = 50;
+ // }
+ //}
+ // jmarshall end
+
+ //
+ ret = botGoalManager.BotChooseNBGItem( bs->gs, bs->origin, bs->inventory, tfl, ltg, range );
+
+ return ret;
+}
+
+
+/*
+=======================
+rvmBot::BotGetRandomPointNearPosition
+=======================
+*/
+void rvmBot::BotGetRandomPointNearPosition( idVec3 point, idVec3& randomPoint, float radius )
+{
+ idAAS* aas = gameLocal.GetBotAAS();
+ idAASFile* file = aas->GetAASFile();
+
+ int areaNum = aas->PointAreaNum(point);
+ const aasArea_t& area = file->GetArea(areaNum);
+ int firstEdge = area.firstEdge;
+ int i = rvRandom::irand(0, area.numEdges);
+
+ const aasEdge_t &edge = file->GetEdge(abs(file->GetEdgeIndex(firstEdge + i)));
+
+ randomPoint = file->GetVertex(edge.vertexNum[0]);
+}
+
+/*
+=======================
+rvmBot::BotMoveInRandomDirection
+=======================
+*/
+int rvmBot::BotMoveInRandomDirection( bot_state_t* bs )
+{
+ rvmBot* ent = gameLocal.entities[bs->client]->Cast();
+
+ //ent->ResetPathFinding();
+
+ float dist = idMath::Distance( ent->GetPhysics()->GetOrigin(), bs->random_move_position );
+
+ if( !ent->PointVisible( bs->random_move_position ) )
+ {
+ dist = 0;
+ }
+
+ if( dist < 25 || bs->random_move_position.Length() == 0 )
+ {
+ BotGetRandomPointNearPosition( ent->GetPhysics()->GetOrigin(), bs->random_move_position, 50.0f );
+ }
+
+ VectorSubtract( bs->random_move_position, ent->GetPhysics()->GetOrigin(), bs->botinput.dir );
+
+ idAngles ang( 0, bs->botinput.dir.ToYaw(), 0 );
+ bs->botinput.speed = pm_runspeed.GetInteger();
+ bs->botinput.dir.Normalize();
+ bs->useRandomPosition = true;
+ bs->botinput.speed = 400; // 200 = walk, 400 = run.
+ return 0;
+}
+
+/*
+============
+rvmBot::ShowHideArea
+============
+*/
+void rvmBot::MoveToCoverPoint(void)
+{
+ int areaNum, numObstacles;
+ idVec3 target;
+ aasGoal_t goal;
+ aasObstacle_t obstacles[10];
+ idVec3 origin = GetOrigin();
+ idAAS* aas = gameLocal.GetBotAAS();
+
+ areaNum = gameLocal.GetBotAAS()->PointReachableAreaNum(origin, aas->DefaultSearchBounds(), (AREA_REACHABLE_WALK | AREA_REACHABLE_FLY));
+ target = aas->AreaCenter(aas->PointAreaNum(gameLocal.GetLocalPlayer()->GetOrigin()));
+
+ // consider the target an obstacle
+ obstacles[0].absBounds = idBounds(target).Expand(16);
+ numObstacles = 1;
+
+ idAASCallback_FindCoverArea findCover(target);
+ if (aas->FindNearestGoal(goal, areaNum, origin, target, TFL_WALK | TFL_AIR, obstacles, numObstacles, findCover))
+ {
+ bs.currentGoal.origin = goal.origin;
+ }
+}
+
+/*
+==================
+rvmBot::BotCheckAttack
+==================
+*/
+void rvmBot::BotCheckAttack( bot_state_t* bs )
+{
+ float points, reactiontime, fov, firethrottle;
+ int attackentity;
+ trace_t bsptrace;
+ //float selfpreservation;
+ idVec3 forward, right, start, end, dir;
+ idAngles angles;
+ weaponinfo_t wi;
+ trace_t trace;
+ //aas_entityinfo_t entinfo;
+ idEntity* entinfo;
+ idVec3 mins( -8, -8, -8 );
+ idVec3 maxs( 8, 8, 8 );
+ idPlayer* self;
+
+ attackentity = bs->enemy;
+ //
+ //BotEntityInfo(attackentity, &entinfo);
+ entinfo = gameLocal.entities[attackentity];
+
+ self = gameLocal.entities[bs->client]->Cast();
+
+ //
+ reactiontime = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_REACTIONTIME, 0, 1 );
+ if( bs->enemysight_time > Bot_Time() - reactiontime )
+ {
+ return;
+ }
+ if( bs->teleport_time > Bot_Time() - reactiontime )
+ {
+ return;
+ }
+
+ //if changing weapons
+ if( bs->weaponchange_time > Bot_Time() - 0.1 )
+ {
+ return;
+ }
+
+ //check fire throttle characteristic
+ if( bs->firethrottlewait_time > Bot_Time() )
+ {
+ return;
+ }
+ // jmarshall - this wasn't original behaivor, but multiplying it by 40 feels better for gameplay
+ firethrottle = botCharacterStatsManager.Characteristic_BFloat( bs->character, CHARACTERISTIC_FIRETHROTTLE, 0, 1 ) * 40.0f;
+ // jmarshall end
+ if( bs->firethrottleshoot_time < Bot_Time() )
+ {
+ if( rvmBotUtil::random() > firethrottle )
+ {
+ bs->firethrottlewait_time = Bot_Time() + firethrottle;
+ bs->firethrottleshoot_time = 0;
+ }
+ else
+ {
+ bs->firethrottleshoot_time = Bot_Time() + 1 - firethrottle;
+ bs->firethrottlewait_time = 0;
+ }
+ }
+
+ VectorSubtract( bs->aimtarget, bs->eye, dir );
+
+// jmarshall - add gauntlet
+ //if (bs->weaponnum == WP_GAUNTLET) {
+ // if (dir.LengthSqr() > Square(60)) {
+ // return;
+ // }
+ //}
+// jmarshall end
+ if( dir.LengthSqr() < Square( 100 ) )
+ {
+ fov = 120;
+ }
+ else
+ {
+ fov = 50;
+ }
+
+ //vectoangles(dir, angles);
+ angles = dir.ToAngles();
+ if( !self->CheckFOV( entinfo->GetPhysics()->GetOrigin() ) )
+ {
+ return;
+ }
+
+ //trap_Trace(&bsptrace, bs->eye, NULL, NULL, bs->aimtarget, bs->client, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);
+ gameLocal.Trace( bsptrace, bs->eye, bs->aimtarget, CONTENTS_SOLID | CONTENTS_PLAYERCLIP, bs->client );
+
+ if( bsptrace.fraction < 1 && bsptrace.c.entityNum != attackentity )
+ {
+ return;
+ }
+
+ //get the weapon info
+ botWeaponInfoManager.BotGetWeaponInfo( bs->ws, bs->weaponnum, &wi );
+
+ //get the start point shooting from
+ VectorCopy( bs->origin, start );
+ start[2] += self->GetViewHeight();// bs->cur_ps.viewheight;
+ bs->viewangles.ToVectors( &forward, &right, NULL );
+
+ start[0] += forward[0] * wi.offset[0] + right[0] * wi.offset[1];
+ start[1] += forward[1] * wi.offset[0] + right[1] * wi.offset[1];
+ start[2] += forward[2] * wi.offset[0] + right[2] * wi.offset[1] + wi.offset[2];
+ //end point aiming at
+ VectorMA( start, 1000, forward, end );
+ //a little back to make sure not inside a very close enemy
+ VectorMA( start, -12, forward, start );
+ //trap_Trace(&trace, start, mins, maxs, end, bs->entitynum, MASK_SHOT);
+ gameLocal.Trace( trace, start, end, MASK_SHOT, bs->entitynum );
+
+ //if the entity is a client
+ if( trace.c.entityNum > 0 && trace.c.entityNum <= MAX_CLIENTS )
+ {
+ if( trace.c.entityNum != attackentity )
+ {
+ // jmarshall - teams
+ //if a teammate is hit
+ //if (BotSameTeam(bs, trace.entityNum))
+ // return;
+ // jmarshall end
+ }
+ }
+ //if won't hit the enemy or not attacking a player (obelisk)
+ if( trace.c.entityNum != attackentity || attackentity >= MAX_CLIENTS )
+ {
+ //if the projectile does radial damage
+ if( wi.proj.damagetype & DAMAGETYPE_RADIAL )
+ {
+ if( trace.fraction * 1000 < wi.proj.radius )
+ {
+ points = ( wi.proj.damage - 0.5 * trace.fraction * 1000 ) * 0.5;
+ if( points > 0 )
+ {
+ return;
+ }
+ }
+ //FIXME: check if a teammate gets radial damage
+ }
+ }
+ //if fire has to be release to activate weapon
+ if( wi.flags & WFL_FIRERELEASED )
+ {
+ if( bs->flags & BFL_ATTACKED )
+ {
+ //trap_EA_Attack(bs->client);
+ bs->botinput.actionflags |= ACTION_ATTACK;
+ }
+ }
+ else
+ {
+ //trap_EA_Attack(bs->client);
+ bs->botinput.actionflags |= ACTION_ATTACK;
+ }
+ bs->flags ^= BFL_ATTACKED;
+}
diff --git a/neo/doom3/game/bots/BotAI_Battle_Attacked.cpp b/neo/doom3/game/bots/BotAI_Battle_Attacked.cpp
new file mode 100644
index 00000000..d873732d
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Battle_Attacked.cpp
@@ -0,0 +1,50 @@
+// BotAI_Battle_Attacked.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+==================
+rvmBot::state_Attacked
+==================
+*/
+stateResult_t rvmBot::state_Attacked(stateParms_t* parms) {
+ // respawn if dead.
+ if (BotIsDead(&bs))
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ if (gameLocal.SysScriptTime() > bs.aggressiveAttackTime || bs.weaponnum == 0) {
+ stateThread.SetState("state_Retreat");
+ return SRESULT_DONE;
+ }
+
+ // Ensure the target is a player.
+ idPlayer *entinfo = gameLocal.entities[bs.enemy]->Cast();
+ if (!entinfo)
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ // If our enemy is dead, search for another LTG.
+ if (EntityIsDead(entinfo))
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ bs.currentGoal.origin = bs.lastenemyorigin;
+
+ //aim at the enemy
+ BotAimAtEnemy(&bs);
+
+ //attack the enemy if possible
+ BotCheckAttack(&bs);
+
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_Battle_Chase.cpp b/neo/doom3/game/bots/BotAI_Battle_Chase.cpp
new file mode 100644
index 00000000..a612b7f8
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Battle_Chase.cpp
@@ -0,0 +1,208 @@
+// BotAI_Battle_Chase.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+
+/*
+=====================
+rvmBot::state_Chase
+=====================
+*/
+stateResult_t rvmBot::state_Chase(stateParms_t* parms)
+{
+ bot_goal_t goal;
+ idVec3 target, dir;
+ //bot_moveresult_t moveresult;
+ float range;
+
+ //if (BotIsObserver(bs)) {
+ // AIEnter_Observer(bs, "battle chase: observer");
+ // return qfalse;
+ //}
+ //
+ ////if in the intermission
+ //if (BotIntermission(bs)) {
+ // AIEnter_Intermission(bs, "battle chase: intermission");
+ // return qfalse;
+ //}
+
+ // respawn if dead.
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //if no enemy
+ if( bs.enemy < 0 )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //if the enemy is visible
+ if( BotEntityVisibleTest( bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy, false ) )
+ {
+ //AIEnter_Battle_Fight(bs, "battle chase");
+ stateThread.SetState("state_BattleFight");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //if there is another enemy
+ if( BotFindEnemy( &bs, -1 ) )
+ {
+ //AIEnter_Battle_Fight(bs, "battle chase: better enemy");
+ //stateThread.SetState("state_BattleFight");
+ stateThread.SetState("state_BattleFight");
+ return SRESULT_DONE_FRAME;
+ }
+ ////there is no last enemy area
+ //if (!bs.lastenemyareanum) {
+ // AIEnter_Seek_LTG(bs, "battle chase: no enemy area");
+ // return qfalse;
+ //}
+// jmarshall
+ //
+ //bs.tfl = TFL_DEFAULT;
+ //if (bot_grapple.integer) bs.tfl |= TFL_GRAPPLEHOOK;
+ ////if in lava or slime the bot should be able to get out
+ //if (BotInLavaOrSlime(bs)) bs.tfl |= TFL_LAVA | TFL_SLIME;
+ ////
+ //if (BotCanAndWantsToRocketJump(bs)) {
+ // bs.tfl |= TFL_ROCKETJUMP;
+ //}
+ //map specific code
+ //BotMapScripts(bs);
+// jmarshall end
+
+ //create the chase goal
+ goal.entitynum = bs.enemy;
+// goal.areanum = bs.lastenemyareanum;
+ VectorCopy( bs.lastenemyorigin, goal.origin );
+
+ goal.mins = idMath::CreateVector( -8, -8, -8 );
+ goal.maxs = idMath::CreateVector( 8, 8, 8 );
+
+ // jmarshall - goal origin is last visible position
+ {
+ // Do a trace between the last_enemy_visible_position and the goal origin,
+ // if for some reason we don't have line of sight to it, switch to LTG.
+ trace_t trace;
+
+ gameLocal.Trace( trace, bs.last_enemy_visible_position, gameLocal.entities[bs.client]->GetOrigin(), CONTENTS_SOLID, 0 );
+
+ if( trace.fraction <= 0.9f )
+ {
+ //AIEnter_Seek_LTG(bs, "can't see last enemy position");
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+ VectorCopy( bs.last_enemy_visible_position, goal.origin );
+ // jmarshall end
+
+ //if the last seen enemy spot is reached the enemy could not be found
+ if( botGoalManager.BotTouchingGoal( bs.origin, &goal ) )
+ {
+ bs.chase_time = 0;
+ }
+
+ //if there's no chase time left
+ if( !bs.chase_time || bs.chase_time < Bot_Time() - 10 )
+ {
+ //AIEnter_Seek_LTG(bs, "battle chase: time out");
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+ //check for nearby goals periodicly
+ if( bs.check_time < Bot_Time() )
+ {
+ bs.check_time = Bot_Time() + 1;
+ range = 150;
+ //
+ if( BotNearbyGoal( &bs, 0, &goal, range ) )
+ {
+ //the bot gets 5 seconds to pick up the nearby goal item
+ bs.nbg_time = Bot_Time() + 0.1 * range + 1;
+ //BotResetLastAvoidReach(bs.ms);
+ //AIEnter_Battle_NBG(bs, "battle chase: nbg");
+ stateThread.SetState("state_BattleNBG");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+
+ BotUpdateBattleInventory( &bs, bs.enemy );
+
+ ////initialize the movement state
+ //BotSetupForMovement(bs);
+
+ //move towards the goal
+ //trap_BotMoveToGoal(&moveresult, bs.ms, &goal, bs.tfl);
+
+ if( idMath::FRandRange( 0.0f, 4.0f ) <= 2.0f )
+ {
+ BotMoveToGoal( &bs, &goal );
+ }
+ else
+ {
+ BotMoveInRandomDirection( &bs );
+ }
+
+
+ //if the movement failed
+ //if (moveresult.failure) {
+ // //reset the avoid reach, otherwise bot is stuck in current area
+ // trap_BotResetAvoidReach(bs.ms);
+ // //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype);
+ // bs.ltg_time = 0;
+ //}
+ ////
+ //BotAIBlocked(bs, &moveresult, qfalse);
+ //
+ //if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW)) {
+ // VectorCopy(moveresult.ideal_viewangles, bs.ideal_viewangles);
+ //}
+
+ if( !( bs.flags & BFL_IDEALVIEWSET ) )
+ {
+ BotAimAtEnemy( &bs );
+ // jmarshall
+ //if (bs.chase_time > FloatTime() - 2) {
+ // BotAimAtEnemy(bs);
+ //}
+ //else {
+ // if (BotMovementViewTarget(bs.ms, &goal, bs.tfl, 300, target)) {
+ // VectorSubtract(target, bs.origin, dir);
+ // vectoangles(dir, bs.viewangles);
+ // }
+ // //else {
+ // // vectoangles(moveresult.movedir, bs.ideal_viewangles);
+ // //}
+ //}
+ // jmarshall end
+// bs.ideal_viewangles[2] *= 0.5; // jmarshall <-- view angles!
+ }
+
+ if (BotEntityVisible(bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy))
+ {
+ //attack the enemy if possible
+ BotCheckAttack(&bs);
+ }
+
+ //if the weapon is used for the bot movement
+ //if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs.weaponnum = moveresult.weapon;
+ //if the bot is in the area the enemy was last seen in
+ //if (bs.areanum == bs.lastenemyareanum) bs.chase_time = 0;
+ //if the bot wants to retreat (the bot could have been damage during the chase)
+ if( BotWantsToRetreat( &bs ) )
+ {
+ //AIEnter_Battle_Retreat(bs, "battle chase: wants to retreat");
+ stateThread.SetState("state_Retreat");
+ return SRESULT_DONE_FRAME;
+ }
+
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_Battle_Fight.cpp b/neo/doom3/game/bots/BotAI_Battle_Fight.cpp
new file mode 100644
index 00000000..99d2c4b2
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Battle_Fight.cpp
@@ -0,0 +1,168 @@
+// BotAI_Battle_Fight.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_BattleFight
+=====================
+*/
+stateResult_t rvmBot::state_BattleFight(stateParms_t* parms)
+{
+ int areanum;
+ idVec3 target;
+ idPlayer* entinfo;
+
+ // respawn if dead.
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //if there is another better enemy
+ if( BotFindEnemy( &bs, bs.enemy ) )
+ {
+ common->DPrintf( "found new better enemy\n" );
+ }
+
+ //if no enemy
+ if( bs.enemy < 0 )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //BotEntityInfo(bs.enemy, &entinfo);
+ entinfo = gameLocal.entities[bs.enemy]->Cast();// &g_entities[bs.enemy];
+
+ //if the enemy is dead
+ if( bs.enemydeath_time )
+ {
+ if( bs.enemydeath_time < Bot_Time() - 1.0 )
+ {
+ bs.enemydeath_time = 0;
+ if( bs.enemysuicide )
+ {
+ // jmarshall - bot chat
+ //BotChat_EnemySuicide(bs);
+ // jmarshall end
+ }
+ // jmarshall - bot chat stand
+ // if (bs.lastkilledplayer == bs.enemy && BotChat_Kill(bs)) {
+ // bs.stand_time = FloatTime() + BotChatTime(bs);
+ // AIEnter_Stand(bs, "battle fight: enemy dead");
+ // }
+ // else {
+ bs.ltg_time = 0;
+ //AIEnter_Seek_LTG(bs, "battle fight: enemy dead");
+ stateThread.SetState("state_SeekLTG");
+ // }
+ // jmarshall end
+ return SRESULT_DONE_FRAME;
+ }
+ }
+ else
+ {
+ if( EntityIsDead( entinfo ) )
+ {
+ bs.enemydeath_time = Bot_Time();
+ }
+ }
+// jmarshall - isinvisible
+ //if the enemy is invisible and not shooting the bot looses track easily
+ //if (entinfo->IsInvisible() && !entinfo->IsShooting()) {
+ // if (rvmBotUtil::random() < 0.2) {
+ // stateThread.SetState("state_SeekLTG");
+ // return;
+ // }
+ //}
+ //
+ target = entinfo->GetOrigin();
+
+ //update the reachability area and origin if possible
+ //areanum = BotPointAreaNum(target);
+ //if (areanum && trap_AAS_AreaReachability(areanum)) {
+ // VectorCopy(target, bs.lastenemyorigin);
+ // bs.lastenemyareanum = areanum;
+ //}
+// bs.lastenemyareanum = areanum;
+
+ //update the attack inventory values
+ BotUpdateBattleInventory( &bs, bs.enemy );
+
+ //if the bot's health decreased
+// jmarshall - bot chat
+ //if (bs.lastframe_health > bs.inventory[INVENTORY_HEALTH]) {
+ // if (BotChat_HitNoDeath(bs)) {
+ // bs.stand_time = FloatTime() + BotChatTime(bs);
+ // AIEnter_Stand(bs, "battle fight: chat health decreased");
+ // return qfalse;
+ // }
+ //}
+
+ //if the bot hit someone
+ //if (bs.cur_ps.persistant[PERS_HITS] > bs.lasthitcount) {
+ // if (BotChat_HitNoKill(bs)) {
+ // bs.stand_time = FloatTime() + BotChatTime(bs);
+ // AIEnter_Stand(bs, "battle fight: chat hit someone");
+ // return qfalse;
+ // }
+ //}
+// jmarshall end
+
+ //if the enemy is not visible
+ if( !BotEntityVisible( bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy ) )
+ {
+ if( BotWantsToChase( &bs ) )
+ {
+ //AIEnter_Battle_Chase(bs, "battle fight: enemy out of sight");
+ stateThread.SetState("state_Chase");
+ return SRESULT_DONE_FRAME;
+ }
+ else
+ {
+ //AIEnter_Seek_LTG(bs, "battle fight: enemy out of sight");
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+ //use holdable items
+ BotBattleUseItems( &bs );
+ //
+ //bs.tfl = TFL_DEFAULT;
+ //if (bot_grapple.integer) bs.tfl |= TFL_GRAPPLEHOOK;
+ ////if in lava or slime the bot should be able to get out
+ //if (BotInLavaOrSlime(bs)) bs.tfl |= TFL_LAVA | TFL_SLIME;
+ ////
+ //if (BotCanAndWantsToRocketJump(bs)) {
+ // bs.tfl |= TFL_ROCKETJUMP;
+ //}
+ //choose the best weapon to fight with
+ BotChooseWeapon( &bs );
+
+ // Move randomly around our AAS area.
+ BotMoveInRandomDirection(&bs);
+
+ //aim at the enemy
+ BotAimAtEnemy( &bs );
+
+ //attack the enemy if possible
+ BotCheckAttack( &bs );
+
+ //if the bot wants to retreat
+ if( !( bs.flags & BFL_FIGHTSUICIDAL ) )
+ {
+ if( BotWantsToRetreat( &bs ) )
+ {
+ //AIEnter_Battle_Retreat(bs, "battle fight: wants to retreat");
+ stateThread.SetState("state_Retreat");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_Battle_NBG.cpp b/neo/doom3/game/bots/BotAI_Battle_NBG.cpp
new file mode 100644
index 00000000..371b2d2c
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Battle_NBG.cpp
@@ -0,0 +1,178 @@
+// BotAI_Battle_NBG.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_BattleNBG
+=====================
+*/
+stateResult_t rvmBot::state_BattleNBG(stateParms_t* parms)
+{
+ int areanum;
+ bot_goal_t goal;
+ //aas_entityinfo_t entinfo;
+ idEntity* entinfo;
+ //bot_moveresult_t moveresult;
+ float attack_skill;
+ idVec3 target, dir;
+
+ //if (BotIsObserver(bs)) {
+ // AIEnter_Observer(bs, "battle nbg: observer");
+ // return qfalse;
+ //}
+ ////if in the intermission
+ //if (BotIntermission(bs)) {
+ // AIEnter_Intermission(bs, "battle nbg: intermission");
+ // return qfalse;
+ //}
+
+ // respawn if dead.
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ // if no enemy.
+ if( bs.enemy < 0 )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //BotEntityInfo(bs.enemy, &entinfo);
+ entinfo = gameLocal.entities[bs.enemy]->Cast();
+ if( entinfo->health <= 0 )
+ {
+ //AIEnter_Seek_NBG(bs, "battle nbg: enemy dead");
+ stateThread.SetState("state_SeekNBG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //bs.tfl = TFL_DEFAULT;
+ //if (bot_grapple.integer) bs.tfl |= TFL_GRAPPLEHOOK;
+ ////if in lava or slime the bot should be able to get out
+ //if (BotInLavaOrSlime(bs)) bs.tfl |= TFL_LAVA | TFL_SLIME;
+ ////
+ //if (BotCanAndWantsToRocketJump(bs)) {
+ // bs.tfl |= TFL_ROCKETJUMP;
+ //}
+ ////map specific code
+ //BotMapScripts(bs);
+
+ //update the last time the enemy was visible
+ if( BotEntityVisible( bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy ) )
+ {
+ bs.enemyvisible_time = Bot_Time();
+ //VectorCopy(entinfo->GetOrigin(), target);
+ target = entinfo->GetOrigin();
+ // if not a player enemy
+ if( bs.enemy >= MAX_CLIENTS )
+ {
+#ifdef MISSIONPACK
+ // if attacking an obelisk
+ if( bs.enemy == redobelisk.entitynum ||
+ bs.enemy == blueobelisk.entitynum )
+ {
+ target[2] += 16;
+ }
+#endif
+ }
+ //update the reachability area and origin if possible
+ //areanum = BotPointAreaNum(target);
+ //if (areanum && trap_AAS_AreaReachability(areanum)) {
+ VectorCopy( target, bs.lastenemyorigin );
+ // bs.lastenemyareanum = areanum;
+ //}
+ }
+
+ //if the bot has no goal or touches the current goal
+ if( !botGoalManager.BotGetTopGoal( bs.gs, &goal ) )
+ {
+ bs.nbg_time = 0;
+ }
+ else if( BotReachedGoal( &bs, &goal ) )
+ {
+ bs.nbg_time = 0;
+ }
+ //
+ if( bs.nbg_time < Bot_Time() )
+ {
+ //pop the current goal from the stack
+ botGoalManager.BotPopGoal( bs.gs );
+ //if the bot still has a goal
+ if( botGoalManager.BotGetTopGoal( bs.gs, &goal ) )
+ {
+ //AIEnter_Battle_Retreat(bs, "battle nbg: time out");
+ stateThread.SetState("state_Retreat");
+ }
+ else
+ {
+ //AIEnter_Battle_Fight(bs, "battle nbg: time out");
+ stateThread.SetState("state_BattleFight");
+ }
+
+ return SRESULT_DONE_FRAME;
+ }
+
+ //move towards the goal
+ BotMoveToGoal( &bs, &goal );
+
+ //initialize the movement state
+ //BotSetupForMovement(bs);
+ ////move towards the goal
+ //trap_BotMoveToGoal(&moveresult, bs.ms, &goal, bs.tfl);
+ ////if the movement failed
+ //if (moveresult.failure) {
+ // //reset the avoid reach, otherwise bot is stuck in current area
+ // trap_BotResetAvoidReach(bs.ms);
+ // //BotAI_Print(PRT_MESSAGE, "movement failure %d\n", moveresult.traveltype);
+ // bs.nbg_time = 0;
+ //}
+ ////
+ //BotAIBlocked(bs, &moveresult, qfalse);
+
+ //update the attack inventory values
+ BotUpdateBattleInventory( &bs, bs.enemy );
+
+ //choose the best weapon to fight with
+ BotChooseWeapon( &bs );
+
+ //if the view is fixed for the movement
+ //if (moveresult.flags & (MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW)) {
+ // VectorCopy(moveresult.ideal_viewangles, bs.ideal_viewangles);
+ //}
+ //else if (!(moveresult.flags & MOVERESULT_MOVEMENTVIEWSET)
+ // && !(bs.flags & BFL_IDEALVIEWSET)) {
+ // attack_skill = trap_Characteristic_BFloat(bs.character, CHARACTERISTIC_ATTACK_SKILL, 0, 1);
+ // //if the bot is skilled anough and the enemy is visible
+ // if (attack_skill > 0.3) {
+ // //&& BotEntityVisible(bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy)
+ // BotAimAtEnemy(bs);
+ // }
+ // else {
+ // if (trap_BotMovementViewTarget(bs.ms, &goal, bs.tfl, 300, target)) {
+ // VectorSubtract(target, bs.origin, dir);
+ // vectoangles(dir, bs.ideal_viewangles);
+ // }
+ // else {
+ // vectoangles(moveresult.movedir, bs.ideal_viewangles);
+ // }
+ // bs.ideal_viewangles[2] *= 0.5;
+ // }
+ //}
+ //if (attack_skill > 0.3) {
+ //&& BotEntityVisible(bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy)
+ BotAimAtEnemy( &bs );
+ //}
+
+ //if the weapon is used for the bot movement
+ //if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs.weaponnum = moveresult.weapon;
+ //attack the enemy if possible
+ BotCheckAttack( &bs );
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_Battle_Retreat.cpp b/neo/doom3/game/bots/BotAI_Battle_Retreat.cpp
new file mode 100644
index 00000000..bcc37517
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Battle_Retreat.cpp
@@ -0,0 +1,144 @@
+// BotAI_Battle_Retreat.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_Retreat
+=====================
+*/
+stateResult_t rvmBot::state_Retreat(stateParms_t* parms)
+{
+ bot_goal_t goal;
+ idPlayer* entinfo;
+ rvmBot* owner;
+ idVec3 target, dir;
+ float attack_skill, range;
+
+ // respawn if dead.
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ // if no enemy.
+ if( bs.enemy < 0 )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ // Ensure the target is a player.
+ entinfo = gameLocal.entities[bs.enemy]->Cast();
+ if( !entinfo )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ owner = gameLocal.entities[bs.entitynum]->Cast();
+
+ // If our enemy is dead, search for another LTG.
+ if( EntityIsDead( entinfo ) )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //if there is another better enemy
+ if( BotFindEnemy( &bs, bs.enemy ) )
+ {
+ common->DPrintf( "found new better enemy\n" );
+ }
+
+ //update the attack inventory values
+ BotUpdateBattleInventory( &bs, bs.enemy );
+
+ //if the bot doesn't want to retreat anymore... probably picked up some nice items
+ if( BotWantsToChase( &bs ) )
+ {
+ //empty the goal stack, when chasing, only the enemy is the goal
+ botGoalManager.BotEmptyGoalStack( bs.gs );
+
+ //go chase the enemy
+ //AIEnter_Battle_Chase(bs, "battle retreat: wants to chase");
+ stateThread.SetState("state_Chase");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //update the last time the enemy was visible
+ if( BotEntityVisible( bs.entitynum, bs.eye, bs.viewangles, 360, bs.enemy ) )
+ {
+ bs.enemyvisible_time = Bot_Time();
+ target = entinfo->GetOrigin();
+ bs.lastenemyorigin = target;
+ }
+
+ //if the enemy is NOT visible for 4 seconds
+ if( bs.enemyvisible_time < Bot_Time() - 4 )
+ {
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+ //else if the enemy is NOT visible
+ else if( bs.enemyvisible_time < Bot_Time() )
+ {
+ //if there is another enemy
+ if( BotFindEnemy( &bs, -1 ) )
+ {
+ //AIEnter_Battle_Fight(bs, "battle retreat: another enemy");
+ stateThread.SetState("state_BattleFight");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+
+ //use holdable items
+ BotBattleUseItems( &bs );
+
+ //get the current long term goal while retreating
+ if( !BotGetItemLongTermGoal( &bs, 0, &bs.currentGoal ) )
+ {
+ //AIEnter_Battle_SuicidalFight(bs, "battle retreat: no way out");
+ stateThread.SetState("state_BattleFight");
+ bs.flags |= BFL_FIGHTSUICIDAL;
+ return SRESULT_DONE_FRAME;
+ }
+
+ //check for nearby goals periodicly
+ if( bs.check_time < Bot_Time() )
+ {
+ bs.check_time = Bot_Time() + 1;
+ range = 150;
+
+ //
+ if( BotNearbyGoal( &bs, 0, &goal, range ) )
+ {
+ //trap_BotResetLastAvoidReach(bs.ms);
+ //time the bot gets to pick up the nearby goal item
+ bs.nbg_time = Bot_Time() + range / 100 + 1;
+ //AIEnter_Battle_NBG(bs, "battle retreat: nbg");
+ stateThread.SetState("state_BattleNBG");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+
+ MoveToCoverPoint();
+
+ if (bot_skill.GetInteger() > 1)
+ {
+ bs.firethrottlewait_time = 0;
+ }
+
+ BotChooseWeapon( &bs );
+
+ BotAimAtEnemy( &bs );
+
+ //attack the enemy if possible
+ BotCheckAttack( &bs );
+
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_SeekLTG.cpp b/neo/doom3/game/bots/BotAI_SeekLTG.cpp
new file mode 100644
index 00000000..8634c5ed
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_SeekLTG.cpp
@@ -0,0 +1,49 @@
+// BotAI_SeekLTG.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_SeekLTG
+=====================
+*/
+stateResult_t rvmBot::state_SeekLTG(stateParms_t* parms)
+{
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ BotGetItemLongTermGoal( &bs, 0, &bs.currentGoal );
+
+ // No Enemy.
+ bs.enemy = -1;
+
+ //if there is an enemy
+ if( BotFindEnemy( &bs, -1 ) )
+ {
+ if( BotWantsToRetreat( &bs ) )
+ {
+ //keep the current long term goal and retreat
+ //AIEnter_Battle_Retreat(bs, "seek ltg: found enemy");
+ stateThread.SetState("state_Retreat");
+ return SRESULT_DONE_FRAME;
+ }
+ else
+ {
+ //trap_BotResetLastAvoidReach(bs.ms);
+ //empty the goal stack
+ botGoalManager.BotEmptyGoalStack( bs.gs );
+
+ //go fight
+ stateThread.SetState("state_BattleFight");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+
+ return SRESULT_WAIT;
+}
diff --git a/neo/doom3/game/bots/BotAI_Seek_NBG.cpp b/neo/doom3/game/bots/BotAI_Seek_NBG.cpp
new file mode 100644
index 00000000..8f41521c
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_Seek_NBG.cpp
@@ -0,0 +1,142 @@
+// BotAI_Seek_NBG.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_SeekNBG
+=====================
+*/
+stateResult_t rvmBot::state_SeekNBG(stateParms_t* parms)
+{
+ bot_goal_t goal;
+ idVec3 target, dir;
+ //bot_moveresult_t moveresult;
+
+ //if (BotIsObserver(bs)) {
+ // AIEnter_Observer(bs, "seek nbg: observer");
+ // return qfalse;
+ //}
+ ////if in the intermission
+ //if (BotIntermission(bs)) {
+ // AIEnter_Intermission(bs, "seek nbg: intermision");
+ // return qfalse;
+ //}
+
+ // respawn if dead.
+ if( BotIsDead( &bs ) )
+ {
+ stateThread.SetState("state_Respawn");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //bs.tfl = TFL_DEFAULT;
+ //if (bot_grapple.integer) bs.tfl |= TFL_GRAPPLEHOOK;
+ ////if in lava or slime the bot should be able to get out
+ //if (BotInLavaOrSlime(bs)) bs.tfl |= TFL_LAVA | TFL_SLIME;
+ ////
+ //if (BotCanAndWantsToRocketJump(bs)) {
+ // bs.tfl |= TFL_ROCKETJUMP;
+ //}
+ ////map specific code
+ //BotMapScripts(bs);
+ //no enemy
+ bs.enemy = -1;
+ //if the bot has no goal
+ if( !botGoalManager.BotGetTopGoal( bs.gs, &goal ) )
+ {
+ bs.nbg_time = 0;
+ }
+ //if the bot touches the current goal
+ else if( BotReachedGoal( &bs, &goal ) )
+ {
+ BotChooseWeapon( &bs );
+ bs.nbg_time = 0;
+ }
+
+ if( bs.nbg_time < Bot_Time() )
+ {
+ //pop the current goal from the stack
+ botGoalManager.BotPopGoal( bs.gs );
+ //check for new nearby items right away
+ //NOTE: we canNOT reset the check_time to zero because it would create an endless loop of node switches
+ bs.check_time = Bot_Time() + 0.05;
+ //go back to seek ltg
+// AIEnter_Seek_LTG(bs, "seek nbg: time out");
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+ }
+
+ //predict obstacles
+ //if (BotAIPredictObstacles(bs, &goal))
+ // return qfalse;
+ ////initialize the movement state
+ //BotSetupForMovement(bs);
+ ////move towards the goal
+ //trap_BotMoveToGoal(&moveresult, bs.ms, &goal, bs.tfl);
+ ////if the movement failed
+ //if (moveresult.failure) {
+ // //reset the avoid reach, otherwise bot is stuck in current area
+ // trap_BotResetAvoidReach(bs.ms);
+ // bs.nbg_time = 0;
+ //}
+ BotMoveToGoal( &bs, &goal );
+
+ //check if the bot is blocked
+ //BotAIBlocked(bs, &moveresult, qtrue);
+ ////
+ //BotClearPath(bs, &moveresult);
+
+// jmarshall - fix look at code.
+ //if the viewangles are used for the movement
+ //if (moveresult.flags & (MOVERESULT_MOVEMENTVIEWSET | MOVERESULT_MOVEMENTVIEW | MOVERESULT_SWIMVIEW)) {
+ // VectorCopy(moveresult.ideal_viewangles, bs.ideal_viewangles);
+ //}
+ ////if waiting for something
+ //else if (moveresult.flags & MOVERESULT_WAITING) {
+ // if (random() < bs.thinktime * 0.8) {
+ // BotRoamGoal(bs, target);
+ // VectorSubtract(target, bs.origin, dir);
+ // vectoangles(dir, bs.ideal_viewangles);
+ // bs.ideal_viewangles[2] *= 0.5;
+ // }
+ //}
+ //else if (!(bs.flags & BFL_IDEALVIEWSET)) {
+ // if (!trap_BotGetSecondGoal(bs.gs, &goal)) trap_BotGetTopGoal(bs.gs, &goal);
+ // if (trap_BotMovementViewTarget(bs.ms, &goal, bs.tfl, 300, target)) {
+ // VectorSubtract(target, bs.origin, dir);
+ // vectoangles(dir, bs.ideal_viewangles);
+ // }
+ // //FIXME: look at cluster portals?
+ // else vectoangles(moveresult.movedir, bs.ideal_viewangles);
+ // bs.ideal_viewangles[2] *= 0.5;
+ //}
+ ////if the weapon is used for the bot movement
+ //if (moveresult.flags & MOVERESULT_MOVEMENTWEAPON) bs.weaponnum = moveresult.weapon;
+// jmarshall end
+ //if there is an enemy
+ if( BotFindEnemy( &bs, -1 ) )
+ {
+ if( BotWantsToRetreat( &bs ) )
+ {
+ //keep the current long term goal and retreat
+ //AIEnter_Battle_NBG(bs, "seek nbg: found enemy");
+ stateThread.SetState("state_BattleNBG");
+ return SRESULT_DONE_FRAME;
+ }
+ else
+ {
+ //trap_BotResetLastAvoidReach(bs.ms);
+ //empty the goal stack
+ botGoalManager.BotEmptyGoalStack( bs.gs );
+ //go fight
+ //AIEnter_Battle_Fight(bs, "seek nbg: found enemy");
+ stateThread.SetState("state_BattleFight");
+ return SRESULT_DONE_FRAME;
+ }
+ }
+ return SRESULT_WAIT;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/BotAI_respawn.cpp b/neo/doom3/game/bots/BotAI_respawn.cpp
new file mode 100644
index 00000000..a054b5af
--- /dev/null
+++ b/neo/doom3/game/bots/BotAI_respawn.cpp
@@ -0,0 +1,33 @@
+// BotAI_respawn.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+/*
+=====================
+rvmBot::state_Respawn
+=====================
+*/
+stateResult_t rvmBot::state_Respawn(stateParms_t* parms)
+{
+ if (parms->stage == 0)
+ {
+ bs.botinput.respawn = true;
+ parms->stage = 1;
+ return SRESULT_WAIT;
+ }
+
+ if (parms->stage == 1)
+ {
+ if (spectating)
+ {
+ return SRESULT_WAIT; // Wait until we have moved from spectator back into the game.
+ }
+ }
+
+ bs.botinput.respawn = false;
+ stateThread.SetState("state_SeekLTG");
+ return SRESULT_DONE_FRAME;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_Input.cpp b/neo/doom3/game/bots/Bot_Input.cpp
new file mode 100644
index 00000000..58e3a9ab
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_Input.cpp
@@ -0,0 +1,160 @@
+// Bot_Input.cpp
+//
+
+#include "precompiled.h"
+#pragma hdrstop
+
+#include "../Game_local.h"
+
+/*
+==============
+rvmBot::BotInputToUserCommand
+==============
+*/
+void rvmBot::BotInputToUserCommand(bot_input_t* bi, usercmd_t* ucmd, int time)
+{
+ idVec3 forward, right;
+
+ short temp;
+ int j;
+
+ //clear the whole structure
+// memset(ucmd, 0, sizeof(usercmd_t));
+ //
+ //common->Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
+ //the duration for the user command in milli seconds
+ //
+ if (bi->actionflags & ACTION_DELAYEDJUMP)
+ {
+ bi->actionflags |= ACTION_JUMP;
+ bi->actionflags &= ~ACTION_DELAYEDJUMP;
+ }
+ //set the buttons
+ if (bi->actionflags & ACTION_RESPAWN)
+ {
+ ucmd->buttons = BUTTON_ATTACK;
+ }
+ if (bi->actionflags & ACTION_ATTACK)
+ {
+ ucmd->buttons |= BUTTON_ATTACK;
+ }
+ //if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK;
+ //if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE;
+ //if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE;
+ if (bi->actionflags & ACTION_WALK)
+ {
+ ucmd->buttons |= BUTTON_RUN;
+ }
+ //if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE;
+ //if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE;
+ //if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG;
+ //if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE;
+ //if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL;
+ //if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME;
+ //
+ ucmd->impulse |= bi->weapon;
+ if (bi->lastWeaponNum != bi->weapon)
+ {
+ //ucmd->flags = UCF_IMPULSE_SEQUENCE;
+ bi->lastWeaponNum = bi->weapon;
+ }
+ else
+ {
+ //ucmd->flags = 0;
+ }
+
+ idAngles botViewAngles = viewAngles;
+
+ {
+ int i;
+ float move;
+ float angMod = (1.0f / 12.0f);
+
+ for (i = 0; i < 3; i++) {
+ move = idMath::AngleDelta(bi->viewangles[i], botViewAngles[i]);
+ botViewAngles[i] += (move * angMod);
+ }
+ }
+
+ //set the view angles
+ //NOTE: the ucmd->angles are the angles WITHOUT the delta angles
+ ucmd->angles[0] = ANGLE2SHORT(botViewAngles[0] - deltaViewAngles[0]);
+ ucmd->angles[1] = ANGLE2SHORT(botViewAngles[1] - deltaViewAngles[1]);
+ ucmd->angles[2] = ANGLE2SHORT(botViewAngles[2] - deltaViewAngles[2]);
+
+ bi->viewangles.ToVectors(&forward, &right, NULL);
+
+ //bot input speed is in the range [0, 400]
+ bi->speed = bi->speed * 127 / 400;
+ //set the view independent movement
+ ucmd->forwardmove = idMath::ClampChar(DotProduct(forward, bi->dir) * bi->speed);
+ ucmd->rightmove = idMath::ClampChar(DotProduct(right, bi->dir) * bi->speed);
+ //ucmd->upmove = abs(forward[2]) * bi->dir[2] * bi->speed;
+
+ //normal keyboard movement
+ if (bi->actionflags & ACTION_MOVEFORWARD)
+ {
+ ucmd->forwardmove += 127;
+ }
+ if (bi->actionflags & ACTION_MOVEBACK)
+ {
+ ucmd->forwardmove -= 127;
+ }
+ if (bi->actionflags & ACTION_MOVELEFT)
+ {
+ ucmd->rightmove -= 127;
+ }
+ if (bi->actionflags & ACTION_MOVERIGHT)
+ {
+ ucmd->rightmove += 127;
+ }
+
+ //jump/moveup
+ if (bi->actionflags & ACTION_JUMP)
+ ucmd->buttons |= BUTTON_JUMP;
+
+ // ucmd->upmove += 127;
+ //
+ ////crouch/movedown
+ //if (bi->actionflags & ACTION_CROUCH)
+ // ucmd->upmove -= 127;
+ //
+ //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
+ //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
+
+ if( bi->respawn )
+ {
+ ucmd->buttons |= BUTTON_ATTACK;
+ }
+}
+
+/*
+================
+rvmBot::ResetUcmd
+================
+*/
+void rvmBot::Bot_ResetUcmd( usercmd_t& ucmd )
+{
+ ucmd.forwardmove = 0;
+ ucmd.rightmove = 0;
+ //ucmd.upmove = 0;
+ ucmd.impulse = 0;
+ //ucmd.flags = 0;
+ memset( &ucmd.buttons, 0, sizeof( ucmd.buttons ) );
+}
+
+
+/*
+========================
+rvmBot::BotInputFrame
+========================
+*/
+void rvmBot::BotInputFrame( idUserCmdMgr& cmdMgr )
+{
+ usercmd_t botcmd = { }; //(usercmd_t&)cmdMgr.GetUserCmdForPlayer(entityNumber); // gameLocal.usercmds[entityNumber];
+
+ Bot_ResetUcmd( botcmd );
+ BotInputToUserCommand( &bs.botinput, &botcmd, gameLocal.time );
+
+ cmdMgr.PutUserCmdForPlayer( entityNumber, botcmd );
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_char.cpp b/neo/doom3/game/bots/Bot_char.cpp
new file mode 100644
index 00000000..7b37901d
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_char.cpp
@@ -0,0 +1,353 @@
+// Bot_char.cpp
+//
+
+#include "precompiled.h"
+#include "../Game_local.h"
+
+idBotCharacterStatsManager botCharacterStatsManager;
+
+/*
+====================
+idBotCharacterStatsManager::idBotCharacterStatsManager
+====================
+*/
+idBotCharacterStatsManager::idBotCharacterStatsManager()
+{
+ default_char_profile = nullptr;
+}
+
+/*
+====================
+idBotCharacterStatsManager::Init
+====================
+*/
+void idBotCharacterStatsManager::Init( void )
+{
+ default_char_profile = BotLoadCharacterFromFile( "bots/default_c.c", 1 );
+ if( default_char_profile == NULL )
+ {
+ common->FatalError( "Failed to load default characteristic def file\n" );
+ }
+}
+
+/*
+============================
+idBotCharacterStatsManager::AllocBotCharacter
+============================
+*/
+bot_character_t* idBotCharacterStatsManager::AllocBotCharacter( void )
+{
+ bot_character_t* ch = NULL;
+
+ for( int i = 0; i < MAX_CHAR_STATS; i++ )
+ {
+ if( charStatsList[i].inUse == false )
+ {
+ charStatsList[i].inUse = true;
+ ch = &charStatsList[i];
+ break;
+ }
+ }
+
+ if( ch == nullptr )
+ {
+ gameLocal.Error( "idBotCharacterStatsManager::AllocBotCharacter: Too many bot characters!" );
+ return nullptr;
+ }
+
+ if( default_char_profile == NULL )
+ {
+ memset( &ch->c[0], 0, MAX_CHARACTERISTICS * sizeof( bot_characteristic_t ) );
+ ch->filename = "";
+ ch->inUse = false;
+ ch->skill = 0;
+ }
+ else
+ {
+ //memcpy( ch, default_char_profile, sizeof( bot_character_t ) + MAX_CHARACTERISTICS * sizeof( bot_characteristic_t ) );
+ *ch = *default_char_profile;
+ }
+
+
+
+ return ch;
+}
+
+/*
+============================
+idBotCharacterStatsManager::FreeCharacterFile
+============================
+*/
+void idBotCharacterStatsManager::FreeCharacterFile( bot_character_t* ch )
+{
+ ch->inUse = false;
+}
+
+/*
+============================
+idBotCharacterStatsManager::BotLoadCharacterFromFile
+============================
+*/
+bot_character_t* idBotCharacterStatsManager::BotLoadCharacterFromFile( const char* charfile, int skill )
+{
+ int indent, index;
+ bool foundcharacter;
+ bot_character_t* ch;
+ idParser parser;
+ idToken token;
+
+ foundcharacter = false;
+
+ // Check to see if we already loaded this bot file.
+ for( int i = 0; i < MAX_CHAR_STATS; i++ )
+ {
+ if(charStatsList[i].inUse && charStatsList[i].filename == charfile )
+ {
+ return &charStatsList[i];
+ }
+ }
+
+ rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
+
+ //a bot character is parsed in two phases
+ if( !parser.LoadFile( charfile ) )
+ {
+ common->Warning( "BotLoadCharacterFromFile: counldn't load %s\n", charfile );
+ return NULL;
+ }
+
+ ch = AllocBotCharacter();
+ ch->filename = charfile;
+
+ while( parser.ReadToken( &token ) )
+ {
+ if( token == "skill" )
+ {
+ if( !parser.ExpectTokenType( TT_NUMBER, 0, &token ) )
+ {
+ FreeCharacterFile( ch );
+ parser.Warning( "Expected token type number\n" );
+ return NULL;
+ }
+
+ if( !parser.ExpectTokenString( "{" ) )
+ {
+ FreeCharacterFile( ch );
+ parser.Warning( "Expected token {\n" );
+ return NULL;
+ }
+
+ //if it's the correct skill
+ if( skill < 0 || token.GetIntValue() == skill )
+ {
+ foundcharacter = true;
+ ch->skill = token.GetIntValue();
+ while( parser.ReadToken( &token ) )
+ {
+ if( token == "}" )
+ {
+ break;
+ }
+
+ if( token.type != TT_NUMBER || !( token.subtype & TT_INTEGER ) )
+ {
+ FreeCharacterFile( ch );
+ parser.Error( "expected integer index, found %s\n", token.c_str() );
+ return NULL;
+ }
+
+ index = token.GetIntValue();
+ if( index < 0 || index > MAX_CHARACTERISTICS )
+ {
+ FreeCharacterFile( ch );
+ parser.Error( "characteristic index out of range [0, %d]\n", MAX_CHARACTERISTICS );
+ return NULL;
+ }
+ // jmarshall - not sure what we loose by removing this check, basically duplicate definition check?
+ //if (ch->c[index].type)
+ //{
+ // G_Error("characteristic %d already initialized\n", index);
+ // trap_PC_FreeSource(source);
+ // BotFreeCharacterStrings(ch);
+ // //FreeMemory(ch);
+ // return NULL;
+ //}
+ // jmarshall end
+
+ if( !parser.ReadToken( &token ) )
+ {
+ FreeCharacterFile( ch );
+ parser.Error( "Unexpected EOF during parse characesistic" );
+ return NULL;
+ }
+
+ if( token.type == TT_NUMBER )
+ {
+ if( token.subtype & TT_FLOAT )
+ {
+ ch->c[index].value._float = token.GetFloatValue();
+ ch->c[index].type = CT_FLOAT;
+ }
+ else
+ {
+ ch->c[index].value.integer = token.GetIntValue();
+ ch->c[index].type = CT_INTEGER;
+ }
+ }
+ else if( token.type == TT_STRING )
+ {
+ token.StripDoubleQuotes();
+ ch->c[index].value.string = token;
+ ch->c[index].type = CT_STRING;
+ }
+ else
+ {
+ FreeCharacterFile( ch );
+ gameLocal.Error( "expected integer, float or string, found %s\n", token.c_str() );
+ return NULL;
+ }
+ }
+ break;
+ }
+ else
+ {
+ indent = 1;
+ while( indent )
+ {
+ if( !parser.ReadToken( &token ) )
+ {
+ FreeCharacterFile( ch );
+ return NULL;
+ }
+ if( token == "{" )
+ {
+ indent++;
+ }
+ else if( token == "}" )
+ {
+ indent--;
+ }
+ }
+ }
+ }
+ else
+ {
+ FreeCharacterFile( ch );
+ gameLocal.Error( "unknown definition %s\n", token.c_str() );
+ return NULL;
+ }
+ }
+
+ if( !foundcharacter )
+ {
+ FreeCharacterFile( ch );
+ return NULL;
+ }
+
+
+ return ch;
+}
+
+
+/*
+============================
+idBotCharacterStatsManager::CheckCharacteristicIndex
+============================
+*/
+int idBotCharacterStatsManager::CheckCharacteristicIndex( bot_character_t* ch, int index )
+{
+ if( index < 0 || index >= MAX_CHARACTERISTICS )
+ {
+ gameLocal.Error( "characteristic %d does not exist\n", index );
+ return false;
+ }
+ if( !ch->c[index].type )
+ {
+ gameLocal.Error( "characteristic %d is not initialized\n", index );
+ return false;
+ }
+ return true;
+}
+
+/*
+============================
+idBotCharacterStatsManager::Characteristic_Float
+============================
+*/
+float idBotCharacterStatsManager::Characteristic_Float( bot_character_t* ch, int index )
+{
+ if( !ch )
+ {
+ return 0;
+ }
+ //check if the index is in range
+ if( !CheckCharacteristicIndex( ch, index ) )
+ {
+ return 0;
+ }
+
+ if( ch->c[index].type == CT_INTEGER )
+ {
+ //an integer will be converted to a float
+ return ( float )ch->c[index].value.integer;
+ }
+ else if( ch->c[index].type == CT_FLOAT )
+ {
+ //floats are just returned
+ return ch->c[index].value._float;
+ }
+
+ //cannot convert a string pointer to a float
+ gameLocal.Error( "characteristic %d is not a float\n", index );
+ return 0;
+}
+
+/*
+============================
+Characteristic_String
+============================
+*/
+void idBotCharacterStatsManager::Characteristic_String( bot_character_t* ch, int index, char* buf, int size )
+{
+ //check if the index is in range
+ if( !CheckCharacteristicIndex( ch, index ) )
+ {
+ return;
+ }
+
+ //an integer will be converted to a float
+ if( ch->c[index].type == CT_STRING )
+ {
+ strcpy( buf, ch->c[index].value.string );
+ buf[size - 1] = '\0';
+ return;
+ }
+ gameLocal.Error( "characteristic %d is not a string\n", index );
+}
+
+/*
+============================
+idBotCharacterStatsManager::Characteristic_BFloat
+============================
+*/
+float idBotCharacterStatsManager::Characteristic_BFloat( bot_character_t* ch, int index, float min, float max )
+{
+ float value;
+
+ if( min > max )
+ {
+ gameLocal.Error( "cannot bound characteristic %d between %f and %f\n", index, min, max );
+ return 0;
+ }
+
+ value = Characteristic_Float( ch, index );
+ if( value < min )
+ {
+ return min;
+ }
+ if( value > max )
+ {
+ return max;
+ }
+ return value;
+}
diff --git a/neo/doom3/game/bots/Bot_char.h b/neo/doom3/game/bots/Bot_char.h
new file mode 100644
index 00000000..e73f29b0
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_char.h
@@ -0,0 +1,153 @@
+// Bot_char.h
+//
+
+#define MAX_CHAR_STATS 256
+
+//========================================================
+//========================================================
+//name
+#define CHARACTERISTIC_NAME 0 //string
+//gender of the bot
+#define CHARACTERISTIC_GENDER 1 //string ("male", "female", "it")
+//attack skill
+// > 0.0 && < 0.2 = don't move
+// > 0.3 && < 1.0 = aim at enemy during retreat
+// > 0.0 && < 0.4 = only move forward/backward
+// >= 0.4 && < 1.0 = circle strafing
+// > 0.7 && < 1.0 = random strafe direction change
+#define CHARACTERISTIC_ATTACK_SKILL 2 //float [0, 1]
+//weapon weight file
+#define CHARACTERISTIC_WEAPONWEIGHTS 3 //string
+//view angle difference to angle change factor
+#define CHARACTERISTIC_VIEW_FACTOR 4 //float <0, 1]
+//maximum view angle change
+#define CHARACTERISTIC_VIEW_MAXCHANGE 5 //float [1, 360]
+//reaction time in seconds
+#define CHARACTERISTIC_REACTIONTIME 6 //float [0, 5]
+//accuracy when aiming
+#define CHARACTERISTIC_AIM_ACCURACY 7 //float [0, 1]
+//weapon specific aim accuracy
+#define CHARACTERISTIC_AIM_ACCURACY_MACHINEGUN 8 //float [0, 1]
+#define CHARACTERISTIC_AIM_ACCURACY_SHOTGUN 9 //float [0, 1]
+#define CHARACTERISTIC_AIM_ACCURACY_ROCKETLAUNCHER 10 //float [0, 1]
+#define CHARACTERISTIC_AIM_ACCURACY_GRENADELAUNCHER 11 //float [0, 1]
+#define CHARACTERISTIC_AIM_ACCURACY_LIGHTNING 12
+#define CHARACTERISTIC_AIM_ACCURACY_PLASMAGUN 13 //float [0, 1]
+#define CHARACTERISTIC_AIM_ACCURACY_RAILGUN 14
+#define CHARACTERISTIC_AIM_ACCURACY_BFG10K 15 //float [0, 1]
+//skill when aiming
+// > 0.0 && < 0.9 = aim is affected by enemy movement
+// > 0.4 && <= 0.8 = enemy linear leading
+// > 0.8 && <= 1.0 = enemy exact movement leading
+// > 0.5 && <= 1.0 = prediction shots when enemy is not visible
+// > 0.6 && <= 1.0 = splash damage by shooting nearby geometry
+#define CHARACTERISTIC_AIM_SKILL 16 //float [0, 1]
+//weapon specific aim skill
+#define CHARACTERISTIC_AIM_SKILL_ROCKETLAUNCHER 17 //float [0, 1]
+#define CHARACTERISTIC_AIM_SKILL_GRENADELAUNCHER 18 //float [0, 1]
+#define CHARACTERISTIC_AIM_SKILL_PLASMAGUN 19 //float [0, 1]
+#define CHARACTERISTIC_AIM_SKILL_BFG10K 20 //float [0, 1]
+//========================================================
+//chat
+//========================================================
+//file with chats
+#define CHARACTERISTIC_CHAT_FILE 21 //string
+//name of the chat character
+#define CHARACTERISTIC_CHAT_NAME 22 //string
+//characters per minute type speed
+#define CHARACTERISTIC_CHAT_CPM 23 //integer [1, 4000]
+//tendency to insult/praise
+#define CHARACTERISTIC_CHAT_INSULT 24 //float [0, 1]
+//tendency to chat misc
+#define CHARACTERISTIC_CHAT_MISC 25 //float [0, 1]
+//tendency to chat at start or end of level
+#define CHARACTERISTIC_CHAT_STARTENDLEVEL 26 //float [0, 1]
+//tendency to chat entering or exiting the game
+#define CHARACTERISTIC_CHAT_ENTEREXITGAME 27 //float [0, 1]
+//tendency to chat when killed someone
+#define CHARACTERISTIC_CHAT_KILL 28 //float [0, 1]
+//tendency to chat when died
+#define CHARACTERISTIC_CHAT_DEATH 29 //float [0, 1]
+//tendency to chat when enemy suicides
+#define CHARACTERISTIC_CHAT_ENEMYSUICIDE 30 //float [0, 1]
+//tendency to chat when hit while talking
+#define CHARACTERISTIC_CHAT_HITTALKING 31 //float [0, 1]
+//tendency to chat when bot was hit but didn't dye
+#define CHARACTERISTIC_CHAT_HITNODEATH 32 //float [0, 1]
+//tendency to chat when bot hit the enemy but enemy didn't dye
+#define CHARACTERISTIC_CHAT_HITNOKILL 33 //float [0, 1]
+//tendency to randomly chat
+#define CHARACTERISTIC_CHAT_RANDOM 34 //float [0, 1]
+//tendency to reply
+#define CHARACTERISTIC_CHAT_REPLY 35 //float [0, 1]
+//========================================================
+//movement
+//========================================================
+//tendency to crouch
+#define CHARACTERISTIC_CROUCHER 36 //float [0, 1]
+//tendency to jump
+#define CHARACTERISTIC_JUMPER 37 //float [0, 1]
+//tendency to walk
+#define CHARACTERISTIC_WALKER 48 //float [0, 1]
+//tendency to jump using a weapon
+#define CHARACTERISTIC_WEAPONJUMPING 38 //float [0, 1]
+//tendency to use the grapple hook when available
+#define CHARACTERISTIC_GRAPPLE_USER 39 //float [0, 1] //use this!!
+//========================================================
+//goal
+//========================================================
+//item weight file
+#define CHARACTERISTIC_ITEMWEIGHTS 40 //string
+//the aggression of the bot
+#define CHARACTERISTIC_AGGRESSION 41 //float [0, 1]
+//the self preservation of the bot (rockets near walls etc.)
+#define CHARACTERISTIC_SELFPRESERVATION 42 //float [0, 1]
+//how likely the bot is to take revenge
+#define CHARACTERISTIC_VENGEFULNESS 43 //float [0, 1] //use this!!
+//tendency to camp
+#define CHARACTERISTIC_CAMPER 44 //float [0, 1]
+//========================================================
+//========================================================
+//tendency to get easy frags
+#define CHARACTERISTIC_EASY_FRAGGER 45 //float [0, 1]
+//how alert the bot is (view distance)
+#define CHARACTERISTIC_ALERTNESS 46 //float [0, 1]
+//how much the bot fires it's weapon
+#define CHARACTERISTIC_FIRETHROTTLE 47 //float [0, 1]
+
+//
+// idBotCharacterStatsManager
+//
+class idBotCharacterStatsManager
+{
+public:
+ idBotCharacterStatsManager();
+
+ // Inits the stats manager.
+ void Init( void );
+
+ // Loads a character file.
+ bot_character_t* BotLoadCharacterFromFile( const char* charfile, int skill );
+
+ // Free character file.
+ void FreeCharacterFile( bot_character_t* ch );
+
+ // Returns the default character stats profile.
+ bot_character_t* GetDefaultCharProfile( void )
+ {
+ return default_char_profile;
+ }
+
+ int CheckCharacteristicIndex( bot_character_t* ch, int index );
+ float Characteristic_Float( bot_character_t* ch, int index );
+ void Characteristic_String( bot_character_t* ch, int index, char* buf, int size );
+ float Characteristic_BFloat( bot_character_t* ch, int index, float min, float max );
+private:
+ bot_character_t* AllocBotCharacter( void );
+
+ bot_character_t* default_char_profile;
+
+ bot_character_t charStatsList[MAX_CHAR_STATS];
+};
+
+extern idBotCharacterStatsManager botCharacterStatsManager;
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_chat.cpp b/neo/doom3/game/bots/Bot_chat.cpp
new file mode 100644
index 00000000..36d440e5
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_chat.cpp
@@ -0,0 +1,54 @@
+// Bot_Chat.cpp
+//
+
+#include "precompiled.h"
+#include "../Game_local.h"
+
+const char *bot_kill_insult[] = {
+ "%s: Try aiming next time %s",
+ "%s: Trust me when I say this %s, you suck.",
+ "%s: Maybe you should go back to go cart racing %s?",
+ "%s: Your technique reminds me of a story %s, a very dull one."
+};
+
+const char* bot_death_insult[] = {
+ "%s: Mmmmmmm. Was it good for you too %s?",
+ "%s: Well I guess %s won the lottery.",
+ "%s: My mother fragged me once %s. Once.",
+ "%s: I'm gonna pull out your bowels %s",
+ "%s: Beginners luck %s... Again",
+ "%s: So %s... you're up to what ... 3 frags an hour?"
+};
+
+const char* bot_death_praise[] = {
+ "%s: %s not bad for an amateur.",
+ "%s: %s alright that was pretty good. Your still a dousche.",
+ "%s: That was definitely ... um ... pretty good %s",
+ "%s: I've seen better %s, but not many.",
+ "%s: Take a moment to reflect on your accomplishment %s",
+ "%s: Your pretty good for a dousche %s"
+};
+
+/*
+====================
+rvmBot::BotSendChatMessage
+====================
+*/
+void rvmBot::BotSendChatMessage(botChat_t chat, const char* targetName) {
+ switch (chat)
+ {
+ case KILL:
+ gameLocal.mpGame.AddChatLine(bot_kill_insult[rvRandom::irand(0, 3)], netname.c_str(), targetName);
+ break;
+ case DEATH:
+ if (rvRandom::irand(0, 10) < 5)
+ {
+ gameLocal.mpGame.AddChatLine(bot_death_insult[rvRandom::irand(0, 5)], netname.c_str(), targetName);
+ }
+ else
+ {
+ gameLocal.mpGame.AddChatLine(bot_death_praise[rvRandom::irand(0, 5)], netname.c_str(), targetName);
+ }
+ break;
+ }
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_chat.h b/neo/doom3/game/bots/Bot_chat.h
new file mode 100644
index 00000000..4a58488a
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_chat.h
@@ -0,0 +1,9 @@
+// Bot_chat.h
+//
+
+#pragma once
+
+enum botChat_t {
+ KILL = 0,
+ DEATH,
+};
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_goal.cpp b/neo/doom3/game/bots/Bot_goal.cpp
new file mode 100644
index 00000000..8217343f
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_goal.cpp
@@ -0,0 +1,2065 @@
+// Bot_goal.cpp
+//
+
+#include "precompiled.h"
+#include "../Game_local.h"
+
+idCVar bot_droppedweight( "bot_droppedweight", "1000", CVAR_CHEAT | CVAR_INTEGER, "" );
+idCVar bot_itemsfile( "bot_itemsfile", "items.c", CVAR_CHEAT, "" );
+
+idBotGoalManager botGoalManager;
+
+/*
+==================
+idBotGoalManager::idBotGoalManager
+==================
+*/
+idBotGoalManager::idBotGoalManager()
+{
+ freelevelitems = NULL;
+ levelitems = NULL;
+}
+
+/*
+==================
+idBotGoalManager::BotGoalStateFromHandle
+==================
+*/
+bot_goalstate_t* idBotGoalManager::BotGoalStateFromHandle( int handle )
+{
+ if( handle <= 0 || handle > MAX_CLIENTS )
+ {
+ gameLocal.Error( "goal state handle %d out of range\n", handle );
+ return NULL;
+ }
+
+ if( !botgoalstates[handle].InUse() )
+ {
+ gameLocal.Error( "invalid goal state %d\n", handle );
+ return NULL;
+ }
+
+ return &botgoalstates[handle];
+}
+
+/*
+==================
+idBotGoalManager::BotInterbreedGoalFuzzyLogic
+==================
+*/
+void idBotGoalManager::BotInterbreedGoalFuzzyLogic( int parent1, int parent2, int child )
+{
+ bot_goalstate_t* p1, * p2, * c;
+
+ p1 = BotGoalStateFromHandle( parent1 );
+ p2 = BotGoalStateFromHandle( parent2 );
+ c = BotGoalStateFromHandle( child );
+
+ botFuzzyWeightManager.InterbreedWeightConfigs( p1->itemweightconfig, p2->itemweightconfig, c->itemweightconfig );
+}
+
+/*
+========================
+idBotGoalManager::BotSaveGoalFuzzyLogic
+========================
+*/
+void idBotGoalManager::BotSaveGoalFuzzyLogic( int goalstate, char* filename )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+
+ //WriteWeightConfig(filename, gs->itemweightconfig);
+}
+
+/*
+========================
+idBotGoalManager::BotMutateGoalFuzzyLogic
+========================
+*/
+void idBotGoalManager::BotMutateGoalFuzzyLogic( int goalstate, float range )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+
+ botFuzzyWeightManager.EvolveWeightConfig( gs->itemweightconfig );
+}
+
+/*
+========================
+idBotGoalManager::ParseItemInfo
+========================
+*/
+void idBotGoalManager::ParseItemInfo( idParser& parser, iteminfo_t* itemInfo )
+{
+ idToken token;
+
+ parser.ExpectTokenString( "{" );
+
+ while( true )
+ {
+ if( !parser.ReadToken( &token ) )
+ {
+ parser.Error( "Unexpected end of file found while parsing weaponinfo!" );
+ }
+
+ if( token == "}" )
+ {
+ break;
+ }
+
+ if( token == "name" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->name = valueToken;
+ }
+ else if( token == "model" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->model = valueToken;
+ }
+ else if( token == "modelindex" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->modelindex = valueToken.GetIntValue();
+ }
+ else if( token == "type" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->type = valueToken.GetIntValue();
+ }
+ else if( token == "index" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->index = valueToken.GetIntValue();
+ }
+ else if( token == "respawntime" )
+ {
+ idToken valueToken;
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->respawntime = valueToken.GetIntValue();
+ }
+ else if( token == "mins" )
+ {
+ float value[3];
+ if( !parser.Parse1DMatrixLegacy( 3, &value[0] ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->mins.Set( value[0], value[1], value[2] );
+ }
+ else if( token == "maxs" )
+ {
+ float value[3];
+ if( !parser.Parse1DMatrixLegacy( 3, &value[0] ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+ itemInfo->maxs.Set( value[0], value[1], value[2] );
+ }
+ else
+ {
+ parser.Error( "ParseProjectileInfo: Unexpected token %s\n", token.c_str() );
+ }
+ }
+}
+
+/*
+========================
+idBotGoalManager::LoadItemConfig
+========================
+*/
+itemconfig_t* idBotGoalManager::LoadItemConfig( char* filename )
+{
+ idToken token;
+ idStr path;
+ itemconfig_t ic;
+ iteminfo_t* ii;
+ idParser source;
+
+ path = filename;
+ rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
+
+ if( !source.LoadFile( path ) )
+ {
+ gameLocal.Error( "counldn't load %s\n", path.c_str() );
+ return NULL;
+ }
+
+ ic.Reset();
+
+ //parse the item config file
+ while( source.ReadToken( &token ) )
+ {
+ if( token == "iteminfo" )
+ {
+ if( ic.numiteminfo >= MAX_BOT_ITEM_INFO )
+ {
+ source.Error( "more than %d item info defined\n", MAX_BOT_ITEM_INFO );
+ return NULL;
+ }
+
+ ii = &ic.iteminfo[ic.numiteminfo];
+ if( !source.ExpectTokenType( TT_STRING, 0, &token ) )
+ {
+ return NULL;
+ }
+
+ token.StripDoubleQuotes();
+ ii->classname = token;
+
+ ParseItemInfo( source, ii );
+
+ ii->number = ic.numiteminfo;
+ ic.numiteminfo++;
+ }
+ else
+ {
+ gameLocal.Error( "unknown definition %s\n", token.c_str() );
+ return NULL;
+ }
+ }
+
+ if( !ic.numiteminfo )
+ {
+ common->Warning( "no item info loaded\n" );
+ }
+
+ common->DPrintf( "loaded %s\n", path );
+ itemconfiglocal = ic;
+ itemconfig = &itemconfiglocal;
+
+ return itemconfig;
+}
+
+/*
+========================
+idBotGoalManager::ItemWeightIndex
+========================
+*/
+int* idBotGoalManager::ItemWeightIndex( weightconfig_t* iwc, itemconfig_t* ic )
+{
+ int* index, i;
+
+ //initialize item weight index
+ index = new int[( sizeof( int ) * ic->numiteminfo )]; // jmarshall: Get this off of the heap!
+
+ for( i = 0; i < ic->numiteminfo; i++ )
+ {
+ index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )ic->iteminfo[i].classname.c_str() );
+ if( index[i] < 0 )
+ {
+ common->Warning( "item info %d \"%s\" has no fuzzy weight\r\n", i, ic->iteminfo[i].classname.c_str() );
+ }
+ }
+ return index;
+}
+
+/*
+========================
+idBotGoalManager::InitLevelItemHeap
+========================
+*/
+void idBotGoalManager::InitLevelItemHeap( void )
+{
+ for( int i = 0; i < MAX_BOT_LEVEL_ITEMS - 1; i++ )
+ {
+ levelitemheap[i].next = &levelitemheap[i + 1];
+ }
+
+ levelitemheap[MAX_BOT_LEVEL_ITEMS - 1].next = NULL;
+
+ freelevelitems = levelitemheap;
+}
+
+/*
+========================
+idBotGoalManager::AllocLevelItem
+========================
+*/
+levelitem_t* idBotGoalManager::AllocLevelItem( void )
+{
+ levelitem_t* li;
+
+ li = freelevelitems;
+ if( !li )
+ {
+ gameLocal.Error( "out of level items\n" );
+ return NULL;
+ }
+
+ freelevelitems = freelevelitems->next;
+ //memset(li, 0, sizeof(levelitem_t));
+ li->Reset();
+ return li;
+}
+
+/*
+========================
+idBotGoalManager::FreeLevelItem
+========================
+*/
+void idBotGoalManager::FreeLevelItem( levelitem_t* li )
+{
+ li->next = freelevelitems;
+ freelevelitems = li;
+}
+
+/*
+========================
+idBotGoalManager::AddLevelItemToList
+========================
+*/
+void idBotGoalManager::AddLevelItemToList( levelitem_t* li )
+{
+ if( levelitems )
+ {
+ levelitems->prev = li;
+ }
+
+ li->prev = NULL;
+ li->next = levelitems;
+ levelitems = li;
+}
+
+/*
+========================
+idBotGoalManager::RemoveLevelItemFromList
+========================
+*/
+void idBotGoalManager::RemoveLevelItemFromList( levelitem_t* li )
+{
+ if( li->prev )
+ {
+ li->prev->next = li->next;
+ }
+ else
+ {
+ levelitems = li->next;
+ }
+ if( li->next )
+ {
+ li->next->prev = li->prev;
+ }
+}
+
+/*
+========================
+idBotGoalManager::BotFreeInfoEntities
+========================
+*/
+void idBotGoalManager::BotFreeInfoEntities( void )
+{
+ maplocation_t* ml, * nextml;
+ campspot_t* cs, * nextcs;
+
+// for (ml = maplocations; ml; ml = nextml)
+// {
+// nextml = ml->next;
+// // jmarshall
+// //FreeMemory(ml);
+// // jmarshall end
+// }
+ maplocations.Clear();
+ //for (cs = campspots; cs; cs = nextcs)
+ //{
+ // nextcs = cs->next;
+ // // jmarshall
+ // //FreeMemory(cs);
+ // // jmarshall end
+ //}
+ campspots.Clear();
+}
+
+/*
+========================
+idBotGoalManager::BotInitInfoEntities
+========================
+*/
+void idBotGoalManager::BotInitInfoEntities( void )
+{
+ //char classname[MAX_EPAIRKEY];
+ idStr classname;
+ maplocation_t* ml;
+ campspot_t* cs;
+
+ BotFreeInfoEntities();
+
+ maplocations.Clear();
+ campspots.Clear();
+
+ for( int i = 0; i < gameLocal.num_entities; i++ )
+ {
+ idEntity* ent = gameLocal.entities[i];
+
+ if( ent == NULL )
+ {
+ continue;
+ }
+
+ classname = ent->spawnArgs.GetString( "classname" );
+
+ //map locations
+ if( classname == "target_location" )
+ {
+ maplocation_t ml;
+ ml.origin = ent->GetPhysics()->GetOrigin();
+ ml.name = ent->GetKey( "message" );
+ maplocations.Append( ml );
+ }
+ //camp spots
+ else if( classname == "info_camp" )
+ {
+ //cs = (campspot_t*)G_AllocClearedMemory(sizeof(campspot_t));
+ campspot_t cs;
+ cs.origin = ent->GetPhysics()->GetOrigin();
+ cs.name = ent->GetKey( "message" );
+ cs.range = ent->GetFloat( "range" );
+ cs.weight = ent->GetFloat( "weight" );
+ cs.wait = ent->GetFloat( "wait" );
+ cs.random = ent->GetFloat( "random" );
+ campspots.Append( cs );
+ }
+ }
+ common->Printf( "%d map locations\n", maplocations.Num() );
+ common->Printf( "%d camp spots\n", campspots.Num() );
+}
+
+/*
+=======================
+idBotGoalManager::InitLevelItems
+=======================
+*/
+void idBotGoalManager::InitLevelItems( void )
+{
+ int i, spawnflags, value;
+ idStr classname;
+ idVec3 origin, end;
+ int ent, goalareanum;
+ itemconfig_t* ic;
+ levelitem_t* li;
+ //bsp_trace_t trace;
+
+ //initialize the map locations and camp spots
+ BotInitInfoEntities();
+
+ //initialize the level item heap
+ InitLevelItemHeap();
+ levelitems = NULL;
+ numlevelitems = 0;
+
+ ic = itemconfig;
+ if( !ic )
+ {
+ return;
+ }
+
+ //update the modelindexes of the item info
+ //for (i = 0; i < ic->numiteminfo; i++)
+ //{
+ // if (!ic->iteminfo[i].modelindex)
+ // {
+ // Log_Write("item %s has modelindex 0", ic->iteminfo[i].classname);
+ // }
+ //}
+
+ //for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
+ for( int idx = 0; idx < gameLocal.num_entities; idx++ )
+ {
+ idItem* ent = gameLocal.entities[idx]->Cast();
+
+ if( ent == nullptr )
+ {
+ continue;
+ }
+
+ spawnflags = ent->GetInt( "spawnflags" );
+ classname = ent->spawnArgs.GetString( "classname" );
+
+ for( i = 0; i < ic->numiteminfo; i++ )
+ {
+ if( !strcmp( classname, ic->iteminfo[i].classname ) )
+ {
+ break;
+ }
+ }
+ if( i >= ic->numiteminfo )
+ {
+ gameLocal.Warning( "entity %s unknown item\r\n", classname.c_str() );
+ continue;
+ }
+
+ //get the origin of the item.
+ origin = ent->GetPhysics()->GetOrigin();
+ goalareanum = 0;
+ // jmarshall - fix floating items
+ //if it is a floating item
+ //if (spawnflags & 1)
+ //{
+ // //if the item is not floating in water
+ // if (!(AAS_PointContents(origin) & CONTENTS_WATER))
+ // {
+ // VectorCopy(origin, end);
+ // end[2] -= 32;
+ // trace = AAS_Trace(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs, end, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP);
+ // //if the item not near the ground
+ // if (trace.fraction >= 1)
+ // {
+ // //if the item is not reachable from a jumppad
+ // goalareanum = AAS_BestReachableFromJumpPadArea(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs);
+ // Log_Write("item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
+ // //G_Printf( "item %s reachable from jumppad area %d\r\n", ic->iteminfo[i].classname, goalareanum);
+ // if (!goalareanum) continue;
+ // } //end if
+ // } //end if
+ //} //end if
+ // jmarshall end
+
+ li = AllocLevelItem();
+ if( !li )
+ {
+ return;
+ }
+
+ li->number = ++numlevelitems;
+ li->timeout = 0;
+ li->item = ent;
+ li->name = classname;
+
+ li->flags = 0;
+ if( ent->GetBool( "notfree" ) )
+ {
+ li->flags |= IFL_NOTFREE;
+ }
+ if( ent->GetBool( "notteam" ) )
+ {
+ li->flags |= IFL_NOTTEAM;
+ }
+ if( ent->GetBool( "notsingle" ) )
+ {
+ li->flags |= IFL_NOTSINGLE;
+ }
+ if( ent->GetBool( "notbot" ) )
+ {
+ li->flags |= IFL_NOTBOT;
+ }
+
+ if( classname == "item_botroam" )
+ {
+ li->flags |= IFL_ROAM;
+ //AAS_FloatForBSPEpairKey(ent, "weight", &li->weight);
+ li->weight = ent->GetFloat( "weight" );
+ }
+
+ ////if not a stationary item
+ //if (!(spawnflags & 1))
+ //{
+ // if (!AAS_DropToFloor(origin, ic->iteminfo[i].mins, ic->iteminfo[i].maxs))
+ // {
+ // G_Printf( "%s in solid at (%1.1f %1.1f %1.1f)\n",
+ // classname, origin[0], origin[1], origin[2]);
+ // } //end if
+ //} //end if
+
+ //item info of the level item
+ li->iteminfo = i;
+
+ //origin of the item
+ VectorCopy( origin, li->origin );
+ VectorCopy( origin, li->goalorigin );
+
+ AddLevelItemToList( li );
+ }
+ gameLocal.Printf( "found %d level items\n", numlevelitems );
+}
+
+/*
+=======================
+idBotGoalManager::BotGoalName
+=======================
+*/
+void idBotGoalManager::BotGoalName( int number, char* name, int size )
+{
+ levelitem_t* li;
+
+ if( !itemconfig )
+ {
+ return;
+ }
+
+ for( li = levelitems; li; li = li->next )
+ {
+ if( li->number == number )
+ {
+ name[size - 1] = '\0';
+ strcpy( name, itemconfig->iteminfo[li->iteminfo].name );
+ return;
+ }
+ }
+}
+
+/*
+=======================
+idBotGoalManager::BotResetAvoidGoals
+=======================
+*/
+void idBotGoalManager::BotResetAvoidGoals( int goalstate )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ memset( gs->avoidgoals, 0, MAX_AVOIDGOALS * sizeof( int ) );
+ memset( gs->avoidgoaltimes, 0, MAX_AVOIDGOALS * sizeof( float ) );
+}
+
+/*
+=======================
+idBotGoalManager::BotResetAvoidGoals
+=======================
+*/
+void idBotGoalManager::BotDumpAvoidGoals( int goalstate )
+{
+ int i;
+ bot_goalstate_t* gs;
+ char name[32];
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ for( i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ if( gs->avoidgoaltimes[i] >= Bot_Time() )
+ {
+ BotGoalName( gs->avoidgoals[i], name, 32 );
+ //Log_Write("avoid goal %s, number %d for %f seconds", name,
+ // gs->avoidgoals[i], gs->avoidgoaltimes[i] - AAS_Time());
+ }
+ }
+}
+
+/*
+=======================
+idBotGoalManager::BotAddToAvoidGoals
+=======================
+*/
+void idBotGoalManager::BotAddToAvoidGoals( bot_goalstate_t* gs, int number, float avoidtime )
+{
+ int i;
+
+ for( i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ //if the avoid goal is already stored
+ if( gs->avoidgoals[i] == number )
+ {
+ gs->avoidgoals[i] = number;
+ gs->avoidgoaltimes[i] = Bot_Time() + avoidtime;
+ return;
+ }
+ }
+
+ for( i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ //if this avoid goal has expired
+ if( gs->avoidgoaltimes[i] < Bot_Time() )
+ {
+ gs->avoidgoals[i] = number;
+ gs->avoidgoaltimes[i] = Bot_Time() + avoidtime;
+ return;
+ }
+ }
+}
+
+/*
+=======================
+idBotGoalManager::BotAddToAvoidGoals
+=======================
+*/
+void idBotGoalManager::BotRemoveFromAvoidGoals( int goalstate, int number )
+{
+ int i;
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+
+ //don't use the goals the bot wants to avoid
+ for( i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ if( gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= Bot_Time() )
+ {
+ gs->avoidgoaltimes[i] = 0;
+ return;
+ }
+ }
+}
+
+/*
+=======================
+idBotGoalManager::BotAvoidGoalTime
+=======================
+*/
+float idBotGoalManager::BotAvoidGoalTime( int goalstate, int number )
+{
+ int i;
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return 0;
+ }
+
+ //don't use the goals the bot wants to avoid
+ for( i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ if( gs->avoidgoals[i] == number && gs->avoidgoaltimes[i] >= Bot_Time() )
+ {
+ return gs->avoidgoaltimes[i] - Bot_Time();
+ }
+ }
+ return 0;
+}
+
+/*
+=======================
+idBotGoalManager::BotSetAvoidGoalTime
+=======================
+*/
+void idBotGoalManager::BotSetAvoidGoalTime( int goalstate, int number, float avoidtime )
+{
+ bot_goalstate_t* gs;
+ levelitem_t* li;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ if( avoidtime < 0 )
+ {
+ if( !itemconfig )
+ {
+ return;
+ }
+
+ for( li = levelitems; li; li = li->next )
+ {
+ if( li->number == number )
+ {
+ avoidtime = itemconfig->iteminfo[li->iteminfo].respawntime;
+ if( !avoidtime )
+ {
+ avoidtime = AVOID_DEFAULT_TIME;
+ }
+ if( avoidtime < AVOID_MINIMUM_TIME )
+ {
+ avoidtime = AVOID_MINIMUM_TIME;
+ }
+ BotAddToAvoidGoals( gs, number, avoidtime );
+ return;
+ }
+ }
+ return;
+ }
+ else
+ {
+ BotAddToAvoidGoals( gs, number, avoidtime );
+ }
+}
+
+/*
+=======================
+idBotGoalManager::BotGetLevelItemGoal
+=======================
+*/
+int idBotGoalManager::BotGetLevelItemGoal( int index, char* name, bot_goal_t* goal )
+{
+ levelitem_t* li;
+
+ if( !itemconfig )
+ {
+ return -1;
+ }
+ li = levelitems;
+ if( index >= 0 )
+ {
+ for( ; li; li = li->next )
+ {
+ if( li->number == index )
+ {
+ li = li->next;
+ break;
+ }
+ }
+ }
+
+ for( ; li; li = li->next )
+ {
+ if( gameLocal.gameType == GAME_SP )
+ {
+ if( li->flags & IFL_NOTSINGLE )
+ {
+ continue;
+ }
+ }
+ else if( gameLocal.gameType >= GAME_TDM )
+ {
+ if( li->flags & IFL_NOTTEAM )
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if( li->flags & IFL_NOTFREE )
+ {
+ continue;
+ }
+ }
+ if( li->flags & IFL_NOTBOT )
+ {
+ continue;
+ }
+
+ if( itemconfig->iteminfo[li->iteminfo].name == name )
+ {
+// goal->areanum = li->goalareanum;
+ goal->origin = li->goalorigin;
+ goal->entitynum = li->item->entityNumber;
+ goal->mins = itemconfig->iteminfo[li->iteminfo].mins;
+ goal->maxs = itemconfig->iteminfo[li->iteminfo].maxs;
+ goal->number = li->number;
+ goal->flags = GFL_ITEM;
+ if( li->timeout )
+ {
+ goal->flags |= GFL_DROPPED;
+ }
+ //G_Printf( "found li %s\n", itemconfig->iteminfo[li->iteminfo].name);
+ return li->number;
+ }
+ }
+ return -1;
+}
+
+/*
+=========================
+idBotGoalManager::BotGetMapLocationGoal
+=========================
+*/
+int idBotGoalManager::BotGetMapLocationGoal( char* name, bot_goal_t* goal )
+{
+ maplocation_t* ml;
+ idVec3 mins( -8, -8, -8 );
+ idVec3 maxs( 8, 8, 8 );
+
+ for( int i = 0; i < maplocations.Num(); i++ )
+ {
+ ml = &maplocations[i];
+ if( ml->name == name )
+ {
+ goal->areanum = ml->areanum;
+ VectorCopy( ml->origin, goal->origin );
+ goal->entitynum = 0;
+ VectorCopy( mins, goal->mins );
+ VectorCopy( maxs, goal->maxs );
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+=========================
+idBotGoalManager::BotGetNextCampSpotGoal
+=========================
+*/
+int idBotGoalManager::BotGetNextCampSpotGoal( int num, bot_goal_t* goal )
+{
+ int i;
+ campspot_t* cs;
+ idVec3 mins( -8, -8, -8 );
+ idVec3 maxs( 8, 8, 8 );
+
+ if( num < 0 )
+ {
+ num = 0;
+ }
+ i = num;
+ for( int d = 0; d < campspots.Num(); d++ )
+ {
+ cs = &campspots[d];
+ if( --i < 0 )
+ {
+ goal->areanum = cs->areanum;
+ VectorCopy( cs->origin, goal->origin );
+ goal->entitynum = 0;
+ VectorCopy( mins, goal->mins );
+ VectorCopy( maxs, goal->maxs );
+ return num + 1;
+ }
+ }
+ return 0;
+}
+
+/*
+============================
+idBotGoalManager::BotFindEntityForLevelItem
+============================
+*/
+void idBotGoalManager::BotFindEntityForLevelItem( levelitem_t* li )
+{
+ int ent, modelindex;
+ itemconfig_t* ic;
+ //aas_entityinfo_t entinfo;
+ idVec3 dir;
+
+ ic = itemconfig;
+ if( !itemconfig )
+ {
+ return;
+ }
+ for( int idx = 0; idx < gameLocal.num_entities; idx++ )
+ {
+ idItem* ent = gameLocal.entities[idx]->Cast();
+
+ if( ent == NULL )
+ {
+ continue;
+ }
+
+ //get the model index of the entity
+ modelindex = ent->GetModelIndex();
+
+ if( !modelindex )
+ {
+ continue;
+ }
+
+ // jmarshall - eval moving objects
+ //get info about the entity
+ //AAS_EntityInfo(ent, &entinfo);
+ //if the entity is still moving
+ //if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
+ // entinfo.origin[1] != entinfo.lastvisorigin[1] ||
+ // entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
+ //
+ // jmarshall end
+ if( ic->iteminfo[li->iteminfo].modelindex == modelindex )
+ {
+ //check if the entity is very close
+ idVec3 dir = li->origin - ent->GetPhysics()->GetOrigin();
+
+ if( dir.Length() < 30 )
+ {
+ //found an entity for this level item
+ li->item = ent;
+ }
+ }
+ }
+}
+
+/*
+========================
+UpdateEntityItems
+========================
+*/
+
+//NOTE: enum entityType_t in bg_public.h
+#define ET_ITEM 2
+
+void idBotGoalManager::UpdateEntityItems( void )
+{
+ int i, modelindex;
+ idVec3 dir;
+ levelitem_t* li, * nextli;
+ itemconfig_t* ic;
+
+ //timeout current entity items if necessary
+ for( li = levelitems; li; li = nextli )
+ {
+ nextli = li->next;
+ //if it is a item that will time out
+ if( li->timeout )
+ {
+ //timeout the item
+ if( li->timeout < Bot_Time() )
+ {
+ RemoveLevelItemFromList( li );
+ FreeLevelItem( li );
+ }
+ }
+ }
+
+ //find new entity items
+ ic = itemconfig;
+ if( !itemconfig )
+ {
+ return;
+ }
+ //
+ for( int idx = 0; idx < gameLocal.num_entities; idx++ )
+ {
+ idItem* ent = gameLocal.entities[idx]->Cast();
+
+ if( ent == nullptr )
+ {
+ continue;
+ }
+
+ //get the model index of the entity
+ modelindex = ent->GetModelIndex();
+
+ if( !modelindex )
+ {
+ continue;
+ }
+ // jmarshall - eval floating items.
+ ////get info about the entity
+ //AAS_EntityInfo(ent, &entinfo);
+ ////FIXME: don't do this
+ ////skip all floating items for now
+ ////if (entinfo.groundent != ENTITYNUM_WORLD) continue;
+ ////if the entity is still moving
+ //if (entinfo.origin[0] != entinfo.lastvisorigin[0] ||
+ // entinfo.origin[1] != entinfo.lastvisorigin[1] ||
+ // entinfo.origin[2] != entinfo.lastvisorigin[2]) continue;
+ // jmarshall end
+
+ //check if the entity is already stored as a level item
+ for( li = levelitems; li; li = li->next )
+ {
+ //if the level item is linked to an entity
+ if( li->item && li->item == ent )
+ {
+ //the entity is re-used if the models are different
+ if( ic->iteminfo[li->iteminfo].modelindex != modelindex )
+ {
+ //remove this level item
+ RemoveLevelItemFromList( li );
+ FreeLevelItem( li );
+ li = NULL;
+ break;
+ }
+ else
+ {
+ //if (entinfo.origin[0] != li->origin[0] ||
+ // entinfo.origin[1] != li->origin[1] ||
+ // entinfo.origin[2] != li->origin[2])
+ //{
+ // VectorCopy(entinfo.origin, li->origin);
+ // //also update the goal area number
+ // li->goalareanum = AAS_BestReachableArea(li->origin,
+ // ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
+ // li->goalorigin);
+ //} //end if
+ li->origin = ent->GetPhysics()->GetOrigin();
+ break;
+ }
+ }
+ }
+ if( li )
+ {
+ continue;
+ }
+
+ //try to link the entity to a level item
+ for( li = levelitems; li; li = li->next )
+ {
+ //if this level item is already linked
+ if( li->item )
+ {
+ continue;
+ }
+ //
+ if( gameLocal.gameType == GAME_SP )
+ {
+ if( li->flags & IFL_NOTSINGLE )
+ {
+ continue;
+ }
+ }
+ else if( gameLocal.gameType >= GAME_TDM )
+ {
+ if( li->flags & IFL_NOTTEAM )
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if( li->flags & IFL_NOTFREE )
+ {
+ continue;
+ }
+ }
+
+ //if the model of the level item and the entity are the same
+ if( ic->iteminfo[li->iteminfo].modelindex == modelindex )
+ {
+ //check if the entity is very close
+ dir = li->origin - ent->GetPhysics()->GetOrigin();
+ if( dir.Length() < 30 )
+ {
+ //found an entity for this level item
+ li->item = ent;
+ //if the origin is different
+ //if (entinfo.origin[0] != li->origin[0] ||
+ // entinfo.origin[1] != li->origin[1] ||
+ // entinfo.origin[2] != li->origin[2])
+ //{
+ // //update the level item origin
+ // VectorCopy(ent->r.currentOrigin, li->origin);
+ // //also update the goal area number
+ // li->goalareanum = AAS_BestReachableArea(li->origin,
+ // ic->iteminfo[li->iteminfo].mins, ic->iteminfo[li->iteminfo].maxs,
+ // li->goalorigin);
+ //} //end if
+
+ //update the level item origin
+ //VectorCopy(ent->r.currentOrigin, li->origin);
+ li->origin = ent->GetPhysics()->GetOrigin();
+ break;
+ }
+ }
+ }
+
+ if( li )
+ {
+ continue;
+ }
+ //check if the model is from a known item
+ for( i = 0; i < ic->numiteminfo; i++ )
+ {
+ if( ic->iteminfo[i].modelindex == modelindex )
+ {
+ break;
+ }
+ }
+
+ //if the model is not from a known item
+ if( i >= ic->numiteminfo )
+ {
+ continue;
+ }
+ //allocate a new level item
+ li = AllocLevelItem();
+
+ if( !li )
+ {
+ continue;
+ }
+
+ //entity number of the level item
+ li->item = ent;
+
+ //number for the level item
+ li->number = ent->entityNumber;
+
+ //set the item info index for the level item
+ li->iteminfo = i;
+
+ //origin of the item
+ //VectorCopy(ent->r.currentOrigin, li->origin);
+ li->origin = ent->GetPhysics()->GetOrigin();
+
+ // jmarshall - fix this, bots, jump pads, and droppable items, bad combo.
+ //get the item goal area and goal origin
+ //li->goalareanum = AAS_BestReachableArea(li->origin,
+ // ic->iteminfo[i].mins, ic->iteminfo[i].maxs,
+ // li->goalorigin);
+ ////never go for items dropped into jumppads
+ //if (AAS_AreaJumpPad(li->goalareanum))
+ //{
+ // FreeLevelItem(li);
+ // continue;
+ //} //end if
+ // jmarshall end
+ //time this item out after 30 seconds
+ //dropped items disappear after 30 seconds
+ li->timeout = Bot_Time() + 30;
+ //add the level item to the list
+ AddLevelItemToList( li );
+ //G_Printf( "found new level item %s\n", ic->iteminfo[i].classname);
+ }
+}
+
+/*
+====================
+idBotGoalManager::BotDumpGoalStack
+====================
+*/
+void idBotGoalManager::BotDumpGoalStack( int goalstate )
+{
+ int i;
+ bot_goalstate_t* gs;
+ char name[32];
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ for( i = 1; i <= gs->goalstacktop; i++ )
+ {
+ BotGoalName( gs->goalstack[i].number, name, 32 );
+ common->Printf( "%d: %s", i, name );
+ }
+}
+
+/*
+====================
+BotPushGoal
+====================
+*/
+void idBotGoalManager::BotPushGoal( int goalstate, bot_goal_t* goal )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ if( gs->goalstacktop >= MAX_GOALSTACK - 1 )
+ {
+ gameLocal.Error( "goal heap overflow\n" );
+ BotDumpGoalStack( goalstate );
+ return;
+ }
+ gs->goalstacktop++;
+ gs->goalstack[gs->goalstacktop] = *goal;
+}
+
+/*
+====================
+idBotGoalManager::BotPopGoal
+====================
+*/
+void idBotGoalManager::BotPopGoal( int goalstate )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ if( gs->goalstacktop > 0 )
+ {
+ gs->goalstacktop--;
+ }
+}
+
+/*
+====================
+idBotGoalManager::BotEmptyGoalStack
+====================
+*/
+void idBotGoalManager::BotEmptyGoalStack( int goalstate )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ gs->goalstacktop = 0;
+}
+
+/*
+====================
+idBotGoalManager::BotGetTopGoal
+====================
+*/
+int idBotGoalManager::BotGetTopGoal( int goalstate, bot_goal_t* goal )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return false;
+ }
+ if( !gs->goalstacktop )
+ {
+ return false;
+ }
+ memcpy( goal, &gs->goalstack[gs->goalstacktop], sizeof( bot_goal_t ) );
+ return true;
+}
+
+/*
+====================
+idBotGoalManager::BotGetSecondGoal
+====================
+*/
+int idBotGoalManager::BotGetSecondGoal( int goalstate, bot_goal_t* goal )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return false;
+ }
+ if( gs->goalstacktop <= 1 )
+ {
+ return false;
+ }
+ memcpy( goal, &gs->goalstack[gs->goalstacktop - 1], sizeof( bot_goal_t ) );
+ return true;
+}
+
+/*
+====================
+idBotGoalManager::BotChooseLTGItem
+====================
+*/
+int idBotGoalManager::BotChooseLTGItem( int goalstate, idVec3 origin, int* inventory, int travelflags )
+{
+ int t, weightnum;
+ float weight, bestweight, avoidtime;
+ iteminfo_t* iteminfo;
+ itemconfig_t* ic;
+ levelitem_t* li, * bestitem;
+ bot_goal_t goal;
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return false;
+ }
+ if( !gs->itemweightconfig )
+ {
+ return false;
+ }
+
+ //get the area the bot is in
+ //areanum = BotReachabilityArea(origin, gs->client);
+ //if the bot is in solid or if the area the bot is in has no reachability links
+ //if (!areanum || !AAS_AreaReachability(areanum))
+ //{
+ // //use the last valid area the bot was in
+ // areanum = gs->lastreachabilityarea;
+ //} //end if
+ //remember the last area with reachabilities the bot was in
+ //gs->lastreachabilityarea = areanum;
+ //if still in solid
+ //if (!areanum)
+ // return qfalse;
+ //the item configuration
+ ic = itemconfig;
+ if( !itemconfig )
+ {
+ return false;
+ }
+ //best weight and item so far
+ bestweight = 0;
+ bestitem = NULL;
+ memset( &goal, 0, sizeof( bot_goal_t ) );
+ //go through the items in the level
+ for( li = levelitems; li; li = li->next )
+ {
+ if( gameLocal.gameType == GAME_SP )
+ {
+ if( li->flags & IFL_NOTSINGLE )
+ {
+ continue;
+ }
+ }
+ else if( gameLocal.gameType >= GAME_TDM )
+ {
+ if( li->flags & IFL_NOTTEAM )
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if( li->flags & IFL_NOTFREE )
+ {
+ continue;
+ }
+ }
+ if( li->flags & IFL_NOTBOT )
+ {
+ continue;
+ }
+ //if the item is not in a possible goal area
+ //if (!li->goalareanum)
+ // continue;
+ //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
+ if( !li->item && !( li->flags & IFL_ROAM ) )
+ {
+ continue;
+ }
+ //get the fuzzy weight function for this item
+ iteminfo = &ic->iteminfo[li->iteminfo];
+ weightnum = gs->itemweightindex[iteminfo->number];
+ if( weightnum < 0 )
+ {
+ continue;
+ }
+
+#ifdef UNDECIDEDFUZZY
+ weight = FuzzyWeightUndecided( inventory, gs->itemweightconfig, weightnum );
+#else
+ weight = botFuzzyWeightManager.FuzzyWeight( inventory, gs->itemweightconfig, weightnum );
+#endif //UNDECIDEDFUZZY
+#ifdef DROPPEDWEIGHT
+ //HACK: to make dropped items more attractive
+ if( li->timeout )
+ {
+ weight += bot_droppedweight.GetFloat(); //GetValueFromLibVar(droppedweight);
+ }
+#endif //DROPPEDWEIGHT
+ //use weight scale for item_botroam
+ if( li->flags & IFL_ROAM )
+ {
+ weight *= li->weight;
+ }
+ //
+ if( weight > 0 )
+ {
+ //get the travel time towards the goal area
+// jmarshall
+ //t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
+ t = gameLocal.TravelTimeToGoal( origin, li->goalorigin );
+ // jmarshall end
+
+ //if the goal is reachable
+ if( t > 0 )
+ {
+ //if this item won't respawn before we get there
+ avoidtime = BotAvoidGoalTime( goalstate, li->number );
+ if( avoidtime - t * 0.009 > 0 )
+ {
+ continue;
+ }
+
+ weight /= ( float )t * TRAVELTIME_SCALE;
+
+ if( weight > bestweight )
+ {
+ bestweight = weight;
+ bestitem = li;
+ }
+ }
+ }
+ }
+
+ //if no goal item found
+ if( !bestitem )
+ {
+ /*
+ //if not in lava or slime
+ if (!AAS_AreaLava(areanum) && !AAS_AreaSlime(areanum))
+ {
+ if (AAS_RandomGoalArea(areanum, travelflags, &goal.areanum, goal.origin))
+ {
+ VectorSet(goal.mins, -15, -15, -15);
+ VectorSet(goal.maxs, 15, 15, 15);
+ goal.entitynum = 0;
+ goal.number = 0;
+ goal.flags = GFL_ROAM;
+ goal.iteminfo = 0;
+ //push the goal on the stack
+ BotPushGoal(goalstate, &goal);
+ //
+ #ifdef DEBUG
+ G_Printf( "chosen roam goal area %d\n", goal.areanum);
+ #endif //DEBUG
+ return qtrue;
+ } //end if
+ } //end if
+ */
+ return false;
+ }
+
+ //create a bot goal for this item
+ iteminfo = &ic->iteminfo[bestitem->iteminfo];
+ VectorCopy( bestitem->goalorigin, goal.origin );
+ VectorCopy( iteminfo->mins, goal.mins );
+ VectorCopy( iteminfo->maxs, goal.maxs );
+// goal.areanum = bestitem->goalareanum;
+ goal.entitynum = bestitem->item->entityNumber;
+ goal.number = bestitem->number;
+ goal.flags = GFL_ITEM;
+ if( bestitem->timeout )
+ {
+ goal.flags |= GFL_DROPPED;
+ }
+ if( bestitem->flags & IFL_ROAM )
+ {
+ goal.flags |= GFL_ROAM;
+ }
+ goal.iteminfo = bestitem->iteminfo;
+ //if it's a dropped item
+ if( bestitem->timeout )
+ {
+ avoidtime = AVOID_DROPPED_TIME;
+ }
+ else
+ {
+ avoidtime = iteminfo->respawntime;
+ if( !avoidtime )
+ {
+ avoidtime = AVOID_DEFAULT_TIME;
+ }
+ if( avoidtime < AVOID_MINIMUM_TIME )
+ {
+ avoidtime = AVOID_MINIMUM_TIME;
+ }
+ }
+
+ //add the chosen goal to the goals to avoid for a while
+ BotAddToAvoidGoals( gs, bestitem->number, avoidtime );
+
+ //push the goal on the stack
+ BotPushGoal( goalstate, &goal );
+
+ return true;
+}
+
+/*
+====================
+idBotGoalManager::BotChooseNBGItem
+====================
+*/
+int idBotGoalManager::BotChooseNBGItem( int goalstate, idVec3 origin, int* inventory, int travelflags, bot_goal_t* ltg, float maxtime )
+{
+ int areanum, t, weightnum, ltg_time;
+ float weight, bestweight, avoidtime;
+ iteminfo_t* iteminfo;
+ itemconfig_t* ic;
+ levelitem_t* li, * bestitem;
+ bot_goal_t goal;
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return false;
+ }
+ if( !gs->itemweightconfig )
+ {
+ return false;
+ }
+ //get the area the bot is in
+// jmarshall
+ areanum = 1; // BotReachabilityArea(origin, gs->client);
+// jmarshall end
+ //if the bot is in solid or if the area the bot is in has no reachability links
+ //if (!areanum || !AAS_AreaReachability(areanum))
+ //{
+ // //use the last valid area the bot was in
+ // areanum = gs->lastreachabilityarea;
+ //} //end if
+ //remember the last area with reachabilities the bot was in
+ gs->lastreachabilityarea = areanum;
+ //if still in solid
+ if( !areanum )
+ {
+ return false;
+ }
+ // jmarshall
+ if( ltg )
+ {
+ //ltg_time = AAS_AreaTravelTimeToGoalArea(areanum, origin, ltg->areanum, travelflags);
+ ltg_time = gameLocal.TravelTimeToGoal( origin, ltg->origin );
+ }
+ else
+ {
+ ltg_time = 99999;
+ }
+ // jmarshall end
+ //the item configuration
+ ic = itemconfig;
+ if( !itemconfig )
+ {
+ return false;
+ }
+ //best weight and item so far
+ bestweight = 0;
+ bestitem = NULL;
+ memset( &goal, 0, sizeof( bot_goal_t ) );
+ //go through the items in the level
+ for( li = levelitems; li; li = li->next )
+ {
+ if( gameLocal.gameType == GAME_SP )
+ {
+ if( li->flags & IFL_NOTSINGLE )
+ {
+ continue;
+ }
+ }
+ else if( gameLocal.gameType >= GAME_TDM )
+ {
+ if( li->flags & IFL_NOTTEAM )
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if( li->flags & IFL_NOTFREE )
+ {
+ continue;
+ }
+ }
+ if( li->flags & IFL_NOTBOT )
+ {
+ continue;
+ }
+ //if the item is in a possible goal area
+// if (!li->goalareanum)
+// continue;
+ //FIXME: is this a good thing? added this for items that never spawned into the game (f.i. CTF flags in obelisk)
+ if( !li->item && !( li->flags & IFL_ROAM ) )
+ {
+ continue;
+ }
+ //get the fuzzy weight function for this item
+ iteminfo = &ic->iteminfo[li->iteminfo];
+ weightnum = gs->itemweightindex[iteminfo->number];
+ if( weightnum < 0 )
+ {
+ continue;
+ }
+ //
+#ifdef UNDECIDEDFUZZY
+ weight = FuzzyWeightUndecided( inventory, gs->itemweightconfig, weightnum );
+#else
+ weight = botFuzzyWeightManager.FuzzyWeight( inventory, gs->itemweightconfig, weightnum );
+#endif //UNDECIDEDFUZZY
+#ifdef DROPPEDWEIGHT
+ //HACK: to make dropped items more attractive
+ if( li->timeout )
+ {
+ weight += bot_droppedweight.GetFloat();
+ }
+#endif //DROPPEDWEIGHT
+ //use weight scale for item_botroam
+ if( li->flags & IFL_ROAM )
+ {
+ weight *= li->weight;
+ }
+ //
+ if( weight > 0 )
+ {
+ //get the travel time towards the goal area
+ //t = AAS_AreaTravelTimeToGoalArea(areanum, origin, li->goalareanum, travelflags);
+ t = gameLocal.TravelTimeToGoal( origin, li->goalorigin );
+
+ //if the goal is reachable
+ if( t > 0 && t < maxtime )
+ {
+ //if this item won't respawn before we get there
+ avoidtime = BotAvoidGoalTime( goalstate, li->number );
+ if( avoidtime - t * 0.009 > 0 )
+ {
+ continue;
+ }
+ //
+ weight /= ( float )t * TRAVELTIME_SCALE;
+ //
+ if( weight > bestweight )
+ {
+ t = 0;
+ if( ltg && !li->timeout )
+ {
+ //get the travel time from the goal to the long term goal
+// jmarshall
+ //t = AAS_AreaTravelTimeToGoalArea(li->goalareanum, li->goalorigin, ltg->areanum, travelflags);
+ t = gameLocal.TravelTimeToGoal( li->goalorigin, ltg->origin );
+ // jmarshall end
+ } //end if
+ //if the travel back is possible and doesn't take too long
+ if( t <= ltg_time )
+ {
+ bestweight = weight;
+ bestitem = li;
+ } //end if
+ } //end if
+ } //end if
+ } //end if
+ } //end for
+ //if no goal item found
+ if( !bestitem )
+ {
+ return false;
+ }
+ //create a bot goal for this item
+ iteminfo = &ic->iteminfo[bestitem->iteminfo];
+ VectorCopy( bestitem->goalorigin, goal.origin );
+ VectorCopy( iteminfo->mins, goal.mins );
+ VectorCopy( iteminfo->maxs, goal.maxs );
+// goal.areanum = bestitem->goalareanum;
+ goal.entitynum = bestitem->item->entityNumber;
+ goal.number = bestitem->number;
+ goal.flags = GFL_ITEM;
+ if( bestitem->timeout )
+ {
+ goal.flags |= GFL_DROPPED;
+ }
+ if( bestitem->flags & IFL_ROAM )
+ {
+ goal.flags |= GFL_ROAM;
+ }
+ goal.iteminfo = bestitem->iteminfo;
+ //if it's a dropped item
+ if( bestitem->timeout )
+ {
+ avoidtime = AVOID_DROPPED_TIME;
+ } //end if
+ else
+ {
+ avoidtime = iteminfo->respawntime;
+ if( !avoidtime )
+ {
+ avoidtime = AVOID_DEFAULT_TIME;
+ }
+ if( avoidtime < AVOID_MINIMUM_TIME )
+ {
+ avoidtime = AVOID_MINIMUM_TIME;
+ }
+ } //end else
+ //add the chosen goal to the goals to avoid for a while
+ BotAddToAvoidGoals( gs, bestitem->number, avoidtime );
+ //push the goal on the stack
+ BotPushGoal( goalstate, &goal );
+ //
+ return true;
+} //end of the function BotChooseNBGItem
+
+
+
+/*
+====================
+idBotGoalManager::BotTouchingGoal
+====================
+*/
+int idBotGoalManager::BotTouchingGoal( idVec3 origin, bot_goal_t* goal )
+{
+ int i;
+ idVec3 boxmins, boxmaxs;
+ idVec3 absmins, absmaxs;
+ idVec3 safety_maxs( 0, 0, 0 ); //{4, 4, 10};
+ idVec3 safety_mins( 0, 0, 0 ); //{-4, -4, 0};
+
+ rvmBot::PresenceTypeBoundingBox( PRESENCE_NORMAL, boxmins, boxmaxs );
+ VectorSubtract( goal->mins, boxmaxs, absmins );
+ VectorSubtract( goal->maxs, boxmins, absmaxs );
+ VectorAdd( absmins, goal->origin, absmins );
+ VectorAdd( absmaxs, goal->origin, absmaxs );
+
+ //make the box a little smaller for safety
+ VectorSubtract( absmaxs, safety_maxs, absmaxs );
+ VectorSubtract( absmins, safety_mins, absmins );
+
+ for( i = 0; i < 3; i++ )
+ {
+ if( origin[i] < absmins[i] || origin[i] > absmaxs[i] )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+========================
+idBotGoalManager::BotItemGoalInVisButNotVisible
+========================
+*/
+int idBotGoalManager::BotItemGoalInVisButNotVisible( int viewer, idVec3 eye, idAngles viewangles, bot_goal_t* goal )
+{
+ //aas_entityinfo_t entinfo;
+ trace_t trace;
+ idVec3 middle;
+
+ if( !( goal->flags & GFL_ITEM ) )
+ {
+ return false;
+ }
+
+ VectorAdd( goal->mins, goal->mins, middle );
+ VectorScale( middle, 0.5, middle );
+ VectorAdd( goal->origin, middle, middle );
+
+ //trap_Trace(&trace, eye, NULL, NULL, middle, viewer, CONTENTS_SOLID);
+ idMat3 axis;
+ axis.Identity();
+ gameLocal.clip.Translation( trace, eye, middle, NULL, axis, CONTENTS_SOLID, NULL );
+
+ //if the goal middle point is visible
+ if( trace.fraction >= 1 )
+ {
+ //the goal entity number doesn't have to be valid
+ //just assume it's valid
+ if( goal->entitynum <= 0 )
+ {
+ return false;
+ }
+ // jmarshall - time delay thing here?
+ //
+ //if the entity data isn't valid
+ //AAS_EntityInfo(goal->entitynum, &entinfo);
+ //NOTE: for some wacko reason entities are sometimes
+ // not updated
+ //if (!entinfo.valid) return qtrue;
+ //if (entinfo.ltime < AAS_Time() - 0.5)
+ // return qtrue;
+ // jmarshall end
+ }
+ return false;
+}
+
+/*
+========================
+BotResetGoalState
+========================
+*/
+void idBotGoalManager::BotResetGoalState( int goalstate )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ //memset(gs->goalstack, 0, MAX_GOALSTACK * sizeof(bot_goal_t));
+ for( int i = 0; i < MAX_GOALSTACK; i++ )
+ {
+ gs->goalstack[i].Reset();
+ }
+ gs->goalstacktop = 0;
+ BotResetAvoidGoals( goalstate );
+}
+
+/*
+========================
+idBotGoalManager::BotResetGoalState
+========================
+*/
+int idBotGoalManager::BotLoadItemWeights( int goalstate, char* filename )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return BLERR_CANNOTLOADITEMWEIGHTS;
+ }
+
+ //load the weight configuration
+ gs->itemweightconfig = botFuzzyWeightManager.ReadWeightConfig( filename );
+ if( !gs->itemweightconfig )
+ {
+ gameLocal.Error( "couldn't load weights\n" );
+ return BLERR_CANNOTLOADITEMWEIGHTS;
+ }
+
+ //if there's no item configuration
+ if( !itemconfig )
+ {
+ return BLERR_CANNOTLOADITEMWEIGHTS;
+ }
+
+ //create the item weight index
+ gs->itemweightindex = ItemWeightIndex( gs->itemweightconfig, itemconfig );
+
+ //everything went ok
+ return BLERR_NOERROR;
+}
+
+/*
+========================
+idBotGoalManager::BotResetGoalState
+========================
+*/
+void idBotGoalManager::BotFreeItemWeights( int goalstate )
+{
+ bot_goalstate_t* gs;
+
+ gs = BotGoalStateFromHandle( goalstate );
+ if( !gs )
+ {
+ return;
+ }
+ if( gs->itemweightconfig )
+ {
+ botFuzzyWeightManager.FreeWeightConfig( gs->itemweightconfig );
+ }
+ // jmarshall - eval
+ //if (gs->itemweightindex)
+ // FreeMemory(gs->itemweightindex);
+ // jmarshall end
+}
+
+/*
+========================
+idBotGoalManager::BotAllocGoalState
+========================
+*/
+int idBotGoalManager::BotAllocGoalState( int client )
+{
+ int i;
+
+ for( i = 1; i <= MAX_CLIENTS; i++ )
+ {
+ if( !botgoalstates[i].InUse() )
+ {
+ botgoalstates[i].Reset();
+ botgoalstates[i].client = client;
+ return i;
+ }
+ }
+ return 0;
+}
+
+/*
+========================
+idBotGoalManager::BotFreeGoalState
+========================
+*/
+void idBotGoalManager::BotFreeGoalState( int handle )
+{
+ if( handle <= 0 || handle > MAX_CLIENTS )
+ {
+ gameLocal.Error( "goal state handle %d out of range\n", handle );
+ return;
+ }
+
+ if( !botgoalstates[handle].InUse() )
+ {
+ gameLocal.Error( "invalid goal state handle %d\n", handle );
+ return;
+ }
+
+ BotFreeItemWeights( handle );
+ // jmarshall
+ //FreeMemory(botgoalstates[handle]);
+ // jmarshall end
+ botgoalstates[handle].Reset();
+}
+
+/*
+========================
+idBotGoalManager::BotSetupGoalAI
+========================
+*/
+int idBotGoalManager::BotSetupGoalAI( void )
+{
+ //load the item configuration
+ itemconfig = LoadItemConfig( ( char* )bot_itemsfile.GetString() );
+ if( !itemconfig )
+ {
+ gameLocal.Error( "couldn't load item config\n" );
+ return BLERR_CANNOTLOADITEMCONFIG;
+ }
+
+ //everything went ok
+ return BLERR_NOERROR;
+}
+
+/*
+========================
+idBotGoalManager::BotSetupGoalAI
+========================
+*/
+void idBotGoalManager::BotShutdownGoalAI( void )
+{
+ int i;
+ // jmarshall
+ //if (itemconfig)
+ // FreeMemory(itemconfig);
+ // jmarshall end
+ itemconfig = NULL;
+ // jmarshall
+ //if (levelitemheap)
+ // FreeMemory(levelitemheap);
+ // jmarshall end
+ //levelitemheap = NULL;
+ for( int i = 0; i < MAX_BOT_LEVEL_ITEMS; i++ )
+ {
+ levelitemheap[i].Reset();
+ }
+
+ freelevelitems = NULL;
+ levelitems = NULL;
+ numlevelitems = 0;
+
+ BotFreeInfoEntities();
+
+ for( i = 1; i <= MAX_CLIENTS; i++ )
+ {
+ if( botgoalstates[i].InUse() )
+ {
+ BotFreeGoalState( i );
+ }
+ }
+}
+
+
+/*
+=======================
+idBotGoalManager::BotNearGoal(
+=======================
+*/
+bool idBotGoalManager::BotNearGoal( idVec3 p1, idVec3 p2 )
+{
+ idVec3 p1_z, p2_z;
+
+ p1_z[0] = p1[0];
+ p1_z[1] = p1[1];
+ p1_z[2] = 0;
+
+ p2_z[0] = p2[0];
+ p2_z[1] = p2[1];
+ p2_z[2] = 0;
+
+ float distToGoal = idMath::Distance( p1_z, p2_z );
+ if( distToGoal <= 50 )
+ {
+ return true;
+ }
+
+ return false;
+}
diff --git a/neo/doom3/game/bots/Bot_goal.h b/neo/doom3/game/bots/Bot_goal.h
new file mode 100644
index 00000000..16263e26
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_goal.h
@@ -0,0 +1,342 @@
+// Bot_goal.h
+//
+
+#define MAX_BOT_ITEM_INFOS 2
+#define MAX_BOT_ITEM_INFO 256
+#define MAX_BOT_LEVEL_ITEMS 256
+
+//#define DEBUG_AI_GOAL
+#ifdef RANDOMIZE
+ #define UNDECIDEDFUZZY
+#endif //RANDOMIZE
+#define DROPPEDWEIGHT
+//minimum avoid goal time
+#define AVOID_MINIMUM_TIME 10
+//default avoid goal time
+#define AVOID_DEFAULT_TIME 30
+//avoid dropped goal time
+#define AVOID_DROPPED_TIME 10
+//
+#define TRAVELTIME_SCALE 0.01
+//item flags
+#define IFL_NOTFREE 1 //not in free for all
+#define IFL_NOTTEAM 2 //not in team play
+#define IFL_NOTSINGLE 4 //not in single player
+#define IFL_NOTBOT 8 //bot should never go for this
+#define IFL_ROAM 16 //bot roam goal
+
+//a bot goal
+struct bot_goal_t
+{
+ bot_goal_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ origin.Zero();
+ areanum = 0;
+ mins.Zero();
+ maxs.Zero();
+ entitynum = 0;
+ number = 0;
+ flags = 0;
+ iteminfo = 0;
+ framenum = -1;
+ }
+
+ int framenum;
+ idVec3 origin; //origin of the goal
+ int areanum; //area number of the goal
+ idVec3 mins, maxs; //mins and maxs of the goal
+ int entitynum; //number of the goal entity
+ int number; //goal number
+ int flags; //goal flags
+ int iteminfo; //item information
+};
+
+//location in the map "target_location"
+struct maplocation_t
+{
+ maplocation_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ origin.Zero();
+ areanum = 0;
+ name = "";
+ next = nullptr;
+ }
+
+ idVec3 origin;
+ int areanum;
+ idStr name;
+ maplocation_t* next;
+};
+
+//camp spots "info_camp"
+struct campspot_t
+{
+ campspot_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ origin.Zero();
+ areanum = 0;
+ name = "";
+ range = 0;
+ weight = 0;
+ random = 0;
+ }
+
+ idVec3 origin;
+ int areanum;
+ idStr name;
+ float range;
+ float weight;
+ float wait;
+ float random;
+};
+
+struct levelitem_t
+{
+ levelitem_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ number = 0;
+ iteminfo = 0;
+ flags = 0;
+ weight = 0;
+ origin.Zero();
+ goalorigin.Zero();
+ item = nullptr;
+ timeout = 0;
+ prev = nullptr;
+ next = nullptr;
+ }
+
+ idStr name;
+ int number; //number of the level item
+ int iteminfo; //index into the item info
+ int flags; //item flags
+ float weight; //fixed roam weight
+ idVec3 origin; //origin of the item
+ idVec3 goalorigin; //goal origin within the area
+ //int entitynum; //entity number
+ idItem* item;
+ float timeout; //item is removed after this time
+ levelitem_t* prev, * next;
+};
+
+struct iteminfo_t
+{
+ iteminfo_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ classname = "";
+ name = "";
+ model = "";
+ modelindex = 0;
+ type = 0;
+ index = 0;
+ respawntime = 0;
+ mins.Zero();
+ maxs.Zero();
+ number = 0;
+ }
+
+ idStr classname; //classname of the item
+ idStr name; //name of the item
+ idStr model; //model of the item
+ int modelindex; //model index
+ int type; //item type
+ int index; //index in the inventory
+ float respawntime; //respawn time
+ idVec3 mins; //mins of the item
+ idVec3 maxs; //maxs of the item
+ int number; //number of the item info
+};
+
+//
+// itemconfig_t
+//
+struct itemconfig_t
+{
+ itemconfig_t()
+ {
+ Reset();
+ }
+
+ void Reset( void )
+ {
+ numiteminfo = 0;
+ for( int i = 0; i < MAX_BOT_ITEM_INFO; i++ )
+ {
+ iteminfo[i].Reset();
+ }
+ }
+
+ int numiteminfo;
+ iteminfo_t iteminfo[MAX_BOT_ITEM_INFO];
+};
+
+//goal state
+struct bot_goalstate_t
+{
+ bot_goalstate_t()
+ {
+ itemweightindex = NULL;
+ Reset();
+ }
+
+ void Reset()
+ {
+ itemweightconfig = NULL;
+
+ if( itemweightindex != NULL )
+ {
+ delete itemweightindex;
+ }
+ itemweightindex = NULL;
+ client = -1;
+ lastreachabilityarea = 0;
+ goalstacktop = 0;
+
+ for( int i = 0; i < MAX_AVOIDGOALS; i++ )
+ {
+ avoidgoals[i] = 0;
+ avoidgoaltimes[i] = 0;
+ }
+ }
+
+ ~bot_goalstate_t()
+ {
+ if( itemweightindex != NULL )
+ {
+ delete itemweightindex;
+ }
+
+ itemweightindex = NULL;
+ }
+
+ bool InUse()
+ {
+ return client != -1;
+ }
+
+ weightconfig_t* itemweightconfig; //weight config
+ int* itemweightindex; //index from item to weight
+
+ int client; //client using this goal state
+ int lastreachabilityarea; //last area with reachabilities the bot was in
+
+ bot_goal_t goalstack[MAX_GOALSTACK]; //goal stack
+ int goalstacktop; //the top of the goal stack
+
+ int avoidgoals[MAX_AVOIDGOALS]; //goals to avoid
+ float avoidgoaltimes[MAX_AVOIDGOALS]; //times to avoid the goals
+};
+
+class idBotGoalManager
+{
+public:
+ idBotGoalManager();
+
+ int BotSetupGoalAI( void );
+
+ void InitLevelItems( void );
+ void UpdateEntityItems( void );
+
+ void BotPushGoal( int goalstate, bot_goal_t* goal );
+ void BotPopGoal( int goalstate );
+ void BotEmptyGoalStack( int goalstate );
+
+ int BotLoadItemWeights( int goalstate, char* filename );
+ void BotResetGoalState( int goalstate );
+
+ int BotItemGoalInVisButNotVisible( int viewer, idVec3 eye, idAngles viewangles, bot_goal_t* goal );
+
+ int BotChooseLTGItem( int goalstate, idVec3 origin, int* inventory, int travelflags );
+ int BotChooseNBGItem( int goalstate, idVec3 origin, int* inventory, int travelflags, bot_goal_t* ltg, float maxtime );
+
+ int BotTouchingGoal( idVec3 origin, bot_goal_t* goal );
+
+ int BotAllocGoalState( int client );
+
+ void BotGoalName( int number, char* name, int size );
+
+ void BotFreeGoalState( int handle );
+ void BotShutdownGoalAI( void );
+ void BotFreeItemWeights( int goalstate );
+public:
+ bool BotNearGoal( idVec3 p1, idVec3 p2 );
+ int BotGetTopGoal( int goalstate, bot_goal_t* goal );
+ int BotGetSecondGoal( int goalstate, bot_goal_t* goal );
+public:
+ void BotDumpGoalStack( int goalstate );
+public:
+ int BotGetLevelItemGoal( int index, char* name, bot_goal_t* goal );
+ void BotSetAvoidGoalTime( int goalstate, int number, float avoidtime );
+ float BotAvoidGoalTime( int goalstate, int number );
+ void BotResetAvoidGoals( int goalstate );
+ void BotDumpAvoidGoals( int goalstate );
+ void BotAddToAvoidGoals( bot_goalstate_t* gs, int number, float avoidtime );
+ void BotRemoveFromAvoidGoals( int goalstate, int number );
+ int BotGetMapLocationGoal( char* name, bot_goal_t* goal );
+ int BotGetNextCampSpotGoal( int num, bot_goal_t* goal );
+ void BotFindEntityForLevelItem( levelitem_t* li );
+private:
+
+ levelitem_t* AllocLevelItem( void );
+ void FreeLevelItem( levelitem_t* li );
+
+ void AddLevelItemToList( levelitem_t* li );
+ void RemoveLevelItemFromList( levelitem_t* li );
+
+ void BotFreeInfoEntities( void );
+
+ void BotInitInfoEntities( void );
+
+ void InitLevelItemHeap( void );
+ int* ItemWeightIndex( weightconfig_t* iwc, itemconfig_t* ic );
+ itemconfig_t* LoadItemConfig( char* filename );
+ void BotSaveGoalFuzzyLogic( int goalstate, char* filename );
+ void BotMutateGoalFuzzyLogic( int goalstate, float range );
+ bot_goalstate_t* BotGoalStateFromHandle( int handle );
+ void BotInterbreedGoalFuzzyLogic( int parent1, int parent2, int child );
+private:
+ void ParseItemInfo( idParser& parser, iteminfo_t* itemInfo );
+private:
+ bot_goalstate_t botgoalstates[MAX_CLIENTS + 1];
+
+ //item configuration
+ itemconfig_t itemconfiglocal;
+ itemconfig_t* itemconfig;
+
+ //level items
+ levelitem_t levelitemheap[MAX_BOT_LEVEL_ITEMS];
+ levelitem_t* freelevelitems;
+ levelitem_t* levelitems;
+ int numlevelitems;
+
+ //map locations
+ idList maplocations;
+ idList campspots;
+};
+
+extern idBotGoalManager botGoalManager;
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_weapons.cpp b/neo/doom3/game/bots/Bot_weapons.cpp
new file mode 100644
index 00000000..ab0bb2cb
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_weapons.cpp
@@ -0,0 +1,492 @@
+// Bot_weapons.cpp
+//
+
+#include "precompiled.h"
+#pragma hdrstop
+
+#include "../Game_local.h"
+
+idCVar bot_weaponsfile( "bot_weaponsfile", "weapons.c", CVAR_GAME | CVAR_CHEAT, "which file to load the weapons weights from" );
+
+idBotWeaponInfoManager botWeaponInfoManager;
+
+/*
+=========================
+idBotWeaponInfoManager::BotValidWeaponNumber
+=========================
+*/
+int idBotWeaponInfoManager::BotValidWeaponNumber( int weaponnum )
+{
+ if( weaponnum <= 0 || weaponnum > BOT_MAX_WEAPONS )
+ {
+ gameLocal.Error( "weapon number out of range\n" );
+ return false;
+ }
+ return true;
+}
+
+/*
+=========================
+idBotWeaponInfoManager::BotWeaponStateFromHandle
+=========================
+*/
+bot_weaponstate_t* idBotWeaponInfoManager::BotWeaponStateFromHandle( int handle )
+{
+ if( handle <= 0 || handle > MAX_CLIENTS )
+ {
+ gameLocal.Error( "move state handle %d out of range\n", handle );
+ }
+
+ return &botweaponstates[handle];
+}
+
+/*
+========================
+idBotWeaponInfoManager::ParseProjectileInfo
+========================
+*/
+void idBotWeaponInfoManager::ParseProjectileInfo( idParser& parser, projectileinfo_t& newProjectileInfo )
+{
+ idToken token;
+ idToken valueToken;
+
+ parser.ExpectTokenString( "{" );
+
+ while( true )
+ {
+ if( !parser.ReadToken( &token ) )
+ {
+ parser.Error( "Unexpected end of file found while parsing weaponinfo!" );
+ }
+
+ if( token == "}" )
+ {
+ break;
+ }
+
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+
+ if( token == "name" )
+ {
+ newProjectileInfo.name = valueToken; // name of the projectile
+ }
+ else if( token == "model" )
+ {
+ newProjectileInfo.model = valueToken; // model of the projectile
+ }
+ else if( token == "flags" )
+ {
+ newProjectileInfo.flags = valueToken.GetIntValue(); // special flags
+ }
+ else if( token == "gravity" )
+ {
+ newProjectileInfo.gravity = valueToken.GetFloatValue(); // amount of gravity applied to the projectile [0,1]
+ }
+ else if( token == "damage" )
+ {
+ newProjectileInfo.damage = valueToken.GetIntValue(); // damage of the projectile
+ }
+ else if( token == "radius" )
+ {
+ newProjectileInfo.radius = valueToken.GetFloatValue(); // radius of damage
+ }
+ else if( token == "visdamage" )
+ {
+ newProjectileInfo.visdamage = valueToken.GetIntValue(); // damage of the projectile to visible entities
+ }
+ else if( token == "damagetype" )
+ {
+ newProjectileInfo.damagetype = valueToken.GetIntValue(); // type of damage (combination of the DAMAGETYPE_? flags)
+ }
+ else if( token == "healthinc" )
+ {
+ newProjectileInfo.healthinc = valueToken.GetIntValue(); // health increase the owner gets
+ }
+ else if( token == "push" )
+ {
+ newProjectileInfo.push = valueToken.GetFloatValue(); // amount a player is pushed away from the projectile impact
+ }
+ else if( token == "detonation" )
+ {
+ newProjectileInfo.detonation = valueToken.GetFloatValue(); // time before projectile explodes after fire pressed
+ }
+ else if( token == "bounce" )
+ {
+ newProjectileInfo.bounce = valueToken.GetFloatValue(); // amount the projectile bounces
+ }
+ else if( token == "bouncefric" )
+ {
+ newProjectileInfo.bouncefric = valueToken.GetFloatValue(); // amount the bounce decreases per bounce
+ }
+ else if( token == "bouncestop" )
+ {
+ newProjectileInfo.bouncestop = valueToken.GetFloatValue(); // minimum bounce value before bouncing stops
+ }
+ else
+ {
+ parser.Error( "ParseProjectileInfo: Unexpected token %s\n", token.c_str() );
+ }
+ }
+}
+
+/*
+========================
+idBotWeaponInfoManager::ParseWeaponInfo
+========================
+*/
+void idBotWeaponInfoManager::ParseWeaponInfo( idParser& parser, weaponinfo_t& newWeaponInfo )
+{
+ idToken token;
+ idToken valueToken;
+
+ parser.ExpectTokenString( "{" );
+
+ while( true )
+ {
+ if( !parser.ReadToken( &token ) )
+ {
+ parser.Error( "Unexpected end of file found while parsing weaponinfo!" );
+ }
+
+ if( token == "}" )
+ {
+ break;
+ }
+
+ if( !parser.ReadToken( &valueToken ) )
+ {
+ parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
+ }
+
+ if( token == "number" )
+ {
+ newWeaponInfo.number = valueToken.GetIntValue(); //weapon number
+ }
+ else if( token == "name" )
+ {
+ newWeaponInfo.name = valueToken; //name of the weapon
+ }
+ else if( token == "level" )
+ {
+ newWeaponInfo.level = valueToken.GetIntValue();
+ }
+ else if( token == "model" )
+ {
+ newWeaponInfo.model = valueToken; //model of the weapon
+ }
+ else if( token == "weaponindex" )
+ {
+ newWeaponInfo.weaponindex = valueToken.GetIntValue(); //index of weapon in inventory
+ }
+ else if( token == "flags" )
+ {
+ newWeaponInfo.flags = valueToken.GetIntValue(); //special flags
+ }
+ else if( token == "projectile" )
+ {
+ newWeaponInfo.projectile = valueToken; //projectile used by the weapon
+ }
+ else if( token == "numprojectiles" )
+ {
+ newWeaponInfo.numprojectiles = valueToken.GetIntValue(); //number of projectiles
+ }
+ else if( token == "hspread" )
+ {
+ newWeaponInfo.hspread = valueToken.GetFloatValue(); //horizontal spread of projectiles (degrees from middle)
+ }
+ else if( token == "vspread" )
+ {
+ newWeaponInfo.vspread = valueToken.GetFloatValue(); //vertical spread of projectiles (degrees from middle)
+ }
+ else if( token == "speed" )
+ {
+ newWeaponInfo.speed = valueToken.GetFloatValue(); //speed of the projectile (0 = instant hit)
+ }
+ else
+ {
+ parser.Error( "ParseWeaponInfo: Unexpected token %s\n", token.c_str() );
+ }
+ }
+}
+
+/*
+========================
+idBotWeaponInfoManager::LoadWeaponConfig
+========================
+*/
+void idBotWeaponInfoManager::LoadWeaponConfig( char* filename )
+{
+ idParser parser;
+ idToken token;
+ int j;
+
+ if( !parser.LoadFile( filename ) )
+ {
+ gameLocal.Error( "Failed to load bot weapon config %s\n", filename );
+ }
+
+ while( parser.ReadToken( &token ) )
+ {
+ if( token == "weaponinfo" )
+ {
+ weaponinfo_t newWeaponInfo;
+ ParseWeaponInfo( parser, newWeaponInfo );
+
+ if( newWeaponInfo.number < 0 || newWeaponInfo.number >= BOT_MAX_WEAPONS )
+ {
+ parser.Error( "weapon info number %d out of range\n", newWeaponInfo.number );
+ return;
+ }
+
+ weaponinfo[newWeaponInfo.number] = newWeaponInfo;
+ weaponinfo[newWeaponInfo.number].valid = true;
+ }
+ else if( token == "projectileinfo" )
+ {
+ projectileinfo_t newProjectileInfo;
+
+ ParseProjectileInfo( parser, newProjectileInfo );
+
+ projectileinfo.Append( newProjectileInfo );
+ }
+ else
+ {
+ parser.Error( "LoadWeaponConfig: Unknown definintion %s", token.c_str() );
+ }
+ }
+
+ // fix up weapons.
+ for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
+ {
+ if( !weaponinfo[i].valid )
+ {
+ continue;
+ }
+
+ if( weaponinfo[i].name.Length() <= 0 )
+ {
+ parser.Error( "weapon %d has no name\n", i );
+ return;
+ }
+
+ if( weaponinfo[i].projectile.Length() <= 0 )
+ {
+ parser.Error( "weapon %s has no projectile\n", weaponinfo[i].name );
+ return;
+ }
+
+ //find the projectile info and copy it to the weapon info
+ for( j = 0; j < projectileinfo.Num(); j++ )
+ {
+ if( projectileinfo[j].name == weaponinfo[i].projectile )
+ {
+ memcpy( &weaponinfo[i].proj, &projectileinfo[j], sizeof( projectileinfo_t ) );
+ break;
+ }
+ }
+ if( j == projectileinfo.Num() )
+ {
+ parser.Error( "weapon %s uses undefined projectile\n", weaponinfo[i].name.c_str() );
+ return;
+ }
+ }
+}
+
+/*
+========================
+idBotWeaponInfoManager::Init
+========================
+*/
+void idBotWeaponInfoManager::Init( void )
+{
+ rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
+
+ LoadWeaponConfig( ( char* )bot_weaponsfile.GetString() );
+}
+
+/*
+=====================
+WeaponWeightIndex
+=====================
+*/
+void idBotWeaponInfoManager::WeaponWeightIndex( weightconfig_t* wwc, bot_weaponstate_t* weaponState )
+{
+ for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
+ {
+ weaponState->weaponweightindex[i] = botFuzzyWeightManager.FindFuzzyWeight( wwc, ( char* )weaponinfo[i].name.c_str() );
+ }
+}
+
+/*
+=====================
+idBotWeaponInfoManager::WeaponWeightIndex
+=====================
+*/
+void idBotWeaponInfoManager::BotFreeWeaponWeights( int weaponstate )
+{
+ bot_weaponstate_t* ws;
+
+ ws = BotWeaponStateFromHandle( weaponstate );
+ if( !ws )
+ {
+ return;
+ }
+// if (ws->weaponweightconfig)
+// FreeWeightConfig(ws->weaponweightconfig);
+ // if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);
+}
+
+/*
+=====================
+BotLoadWeaponWeights
+=====================
+*/
+int idBotWeaponInfoManager::BotLoadWeaponWeights( int weaponstate, char* filename )
+{
+ bot_weaponstate_t* ws;
+
+ ws = BotWeaponStateFromHandle( weaponstate );
+ if( !ws )
+ {
+ return BLERR_CANNOTLOADWEAPONWEIGHTS;
+ }
+
+ BotFreeWeaponWeights( weaponstate );
+
+ ws->weaponweightconfig = botFuzzyWeightManager.ReadWeightConfig( filename );
+ if( !ws->weaponweightconfig )
+ {
+ gameLocal.Error( "couldn't load weapon config %s\n", filename );
+ return BLERR_CANNOTLOADWEAPONWEIGHTS;
+ }
+
+ WeaponWeightIndex( ws->weaponweightconfig, ws );
+ return BLERR_NOERROR;
+}
+
+/*
+=====================
+idBotWeaponInfoManager::BotGetWeaponInfo
+=====================
+*/
+void idBotWeaponInfoManager::BotGetWeaponInfo( int weaponstate, int weapon, weaponinfo_t* weaponinfo )
+{
+ bot_weaponstate_t* ws;
+
+ if( !BotValidWeaponNumber( weapon ) )
+ {
+ return;
+ }
+ ws = BotWeaponStateFromHandle( weaponstate );
+ if( !ws )
+ {
+ return;
+ }
+
+ *weaponinfo = this->weaponinfo[weapon];
+}
+
+/*
+=====================
+idBotWeaponInfoManager::BotChooseBestFightWeapon
+=====================
+*/
+int idBotWeaponInfoManager::BotChooseBestFightWeapon( int weaponstate, int* inventory )
+{
+ int i, index, bestweapon;
+ float weight, bestweight;
+ bot_weaponstate_t* ws;
+
+ ws = BotWeaponStateFromHandle( weaponstate );
+ if( !ws )
+ {
+ return 0;
+ }
+
+ //if the bot has no weapon weight configuration
+ if( !ws->weaponweightconfig )
+ {
+ return 0;
+ }
+
+ bestweight = 0;
+ bestweapon = 0;
+ for( i = 0; i < BOT_MAX_WEAPONS; i++ )
+ {
+ if( !weaponinfo[i].valid )
+ {
+ continue;
+ }
+ index = ws->weaponweightindex[i];
+ if( index < 0 )
+ {
+ continue;
+ }
+ weight = botFuzzyWeightManager.FuzzyWeight( inventory, ws->weaponweightconfig, index );
+ if( weight > bestweight )
+ {
+ bestweight = weight;
+ bestweapon = i;
+ }
+ }
+ return bestweapon;
+}
+
+/*
+=====================
+idBotWeaponInfoManager::BotResetWeaponState
+=====================
+*/
+void idBotWeaponInfoManager::BotResetWeaponState( int weaponstate )
+{
+ weightconfig_t* weaponweightconfig;
+ int* weaponweightindex;
+ bot_weaponstate_t* ws;
+
+ ws = BotWeaponStateFromHandle( weaponstate );
+ if( !ws )
+ {
+ return;
+ }
+ weaponweightconfig = ws->weaponweightconfig;
+ weaponweightindex = ws->weaponweightindex;
+
+ //Com_Memset(ws, 0, sizeof(bot_weaponstate_t));
+ ws->weaponweightconfig = weaponweightconfig;
+ memcpy( ws->weaponweightindex, weaponweightindex, sizeof( int ) * BOT_MAX_WEAPONS );
+}
+
+/*
+=====================
+idBotWeaponInfoManager::BotAllocWeaponState
+=====================
+*/
+int idBotWeaponInfoManager::BotAllocWeaponState( void )
+{
+ int i;
+
+ for( i = 1; i <= MAX_CLIENTS; i++ )
+ {
+ if( !botweaponstates[i].inUse )
+ {
+ botweaponstates[i].Reset();
+ botweaponstates[i].inUse = true;
+ return i;
+ }
+ }
+ return 0;
+}
+
+/*
+=====================
+idBotWeaponInfoManager::BotAllocWeaponState
+=====================
+*/
+void idBotWeaponInfoManager::BotFreeWeaponState( int ws )
+{
+ botweaponstates[ws].inUse = false;
+ botweaponstates[ws].Reset();
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_weapons.h b/neo/doom3/game/bots/Bot_weapons.h
new file mode 100644
index 00000000..c3b42849
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_weapons.h
@@ -0,0 +1,161 @@
+// Bot_weapons.h
+//
+
+#pragma once
+
+#define BOT_MAX_WEAPONS 32
+
+//projectile flags
+#define PFL_WINDOWDAMAGE 1 //projectile damages through window
+#define PFL_RETURN 2 //set when projectile returns to owner
+//weapon flags
+#define WFL_FIRERELEASED 1 //set when projectile is fired with key-up event
+//damage types
+#define DAMAGETYPE_IMPACT 1 //damage on impact
+#define DAMAGETYPE_RADIAL 2 //radial damage
+#define DAMAGETYPE_VISIBLE 4 //damage to all entities visible to the projectile
+
+struct projectileinfo_t
+{
+ projectileinfo_t()
+ {
+ name = "";
+ model = "";
+ flags = 0;
+ gravity = 0;
+ damage = 0;
+ radius = 0;
+ visdamage = 0;
+ damagetype = 0;
+ healthinc = 0;
+ push = 0;
+ detonation = 0;
+ bounce = 0;
+ bouncefric = 0;
+ bouncestop = 0;
+ }
+ idStr name;
+ idStr model;
+ int flags;
+ float gravity;
+ int damage;
+ float radius;
+ int visdamage;
+ int damagetype;
+ int healthinc;
+ float push;
+ float detonation;
+ float bounce;
+ float bouncefric;
+ float bouncestop;
+};
+
+struct weaponinfo_t
+{
+ weaponinfo_t()
+ {
+ valid = 0;
+ number = 0;
+ name = "";
+ model = "";
+ level = 0;
+ weaponindex = 0;
+ flags = 0;
+ projectile = "";
+ numprojectiles = 0;
+ hspread = 0;
+ vspread = 0;
+ speed = 0;
+ acceleration = 0;
+ recoil.Zero();
+ offset.Zero();
+ angleoffset.Zero();
+ extrazvelocity = 0;
+ ammoamount = 0;
+ ammoindex = 0;
+ activate = 0;
+ reload = 0;
+ spinup = 0;
+ spindown = 0;
+ }
+
+ int valid; //true if the weapon info is valid
+ int number; //number of the weapon
+ idStr name;
+ idStr model;
+ int level;
+ int weaponindex;
+ int flags;
+ idStr projectile;
+ int numprojectiles;
+ float hspread;
+ float vspread;
+ float speed;
+ float acceleration;
+ idVec3 recoil;
+ idVec3 offset;
+ idVec3 angleoffset;
+ float extrazvelocity;
+ int ammoamount;
+ int ammoindex;
+ float activate;
+ float reload;
+ float spinup;
+ float spindown;
+ projectileinfo_t proj; //pointer to the used projectile
+};
+
+struct bot_weaponstate_t
+{
+ bot_weaponstate_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ inUse = false;
+ weaponweightconfig = NULL;
+ for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
+ {
+ weaponweightindex[i] = 0;
+ }
+ }
+
+ bool inUse;
+ weightconfig_t* weaponweightconfig;
+ int weaponweightindex[BOT_MAX_WEAPONS]; //weapon weight index
+};
+
+//
+// idBotWeaponInfoManager
+//
+class idBotWeaponInfoManager
+{
+public:
+ void Init( void );
+
+ int BotLoadWeaponWeights( int weaponstate, char* filename );
+ void BotGetWeaponInfo( int weaponstate, int weapon, weaponinfo_t* weaponinfo );
+ int BotChooseBestFightWeapon( int weaponstate, int* inventory );
+ void BotResetWeaponState( int weaponstate );
+ int BotAllocWeaponState( void );
+ void BotFreeWeaponState( int ws );
+private:
+ int BotValidWeaponNumber( int weaponnum );
+ bot_weaponstate_t* BotWeaponStateFromHandle( int handle );
+ void WeaponWeightIndex( weightconfig_t* wwc, bot_weaponstate_t* weaponState );
+ void BotFreeWeaponWeights( int weaponstate );
+private:
+ void LoadWeaponConfig( char* filename );
+
+ void ParseWeaponInfo( idParser& parser, weaponinfo_t& newWeaponInfo );
+ void ParseProjectileInfo( idParser& parser, projectileinfo_t& newProjectileInfo );
+private:
+ idList projectileinfo;
+ bot_weaponstate_t botweaponstates[MAX_CLIENTS + 1];
+
+ weaponinfo_t weaponinfo[BOT_MAX_WEAPONS];
+};
+
+extern idBotWeaponInfoManager botWeaponInfoManager;
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_weights.cpp b/neo/doom3/game/bots/Bot_weights.cpp
new file mode 100644
index 00000000..ad27de73
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_weights.cpp
@@ -0,0 +1,915 @@
+// Bot_wieghts.cpp
+//
+
+#include "precompiled.h"
+#pragma hdrstop
+
+#include "../Game_local.h"
+
+idBotFuzzyWeightManager botFuzzyWeightManager;
+
+/*
+========================
+idBotFuzzyWeightManager::Init
+========================
+*/
+void idBotFuzzyWeightManager::Init( void )
+{
+ memset( &fuzzyseperators[0], 0, sizeof( fuzzyseperators ) );
+}
+
+/*
+========================
+idBotFuzzyWeightManager::AllocFuzzyWeight
+========================
+*/
+fuzzyseperator_t* idBotFuzzyWeightManager::AllocFuzzyWeight( void )
+{
+ for( int i = 0; i < MAX_FUZZY_OPERATORS; i++ )
+ {
+ if( fuzzyseperators[i].inUse == false )
+ {
+ memset( &fuzzyseperators[i], 0, sizeof( fuzzyseperator_t ) );
+ fuzzyseperators[i].inUse = true;
+ return &fuzzyseperators[i];
+ }
+ }
+
+ gameLocal.Error( "AllocFuzzyWeight: Not enough fuzzy weights\n" );
+ return NULL;
+}
+
+/*
+========================
+idBotFuzzyWeightManager::ReadValue
+========================
+*/
+bool idBotFuzzyWeightManager::ReadValue( idParser& source, float* value )
+{
+ idToken token;
+
+ if( !source.ReadToken( &token ) )
+ {
+ return false;
+ }
+
+ if( token == "-" )
+ {
+ source.Warning( "negative value set to zero\n" );
+ if( !source.ExpectTokenType( TT_NUMBER, 0, &token ) )
+ {
+ return false;
+ }
+ }
+
+ if( token.type != TT_NUMBER )
+ {
+ source.Error( "invalid return value %s\n", token.c_str() );
+ return false;
+ }
+
+ *value = token.GetFloatValue();
+
+ return true;
+}
+
+/*
+===================
+idBotFuzzyWeightManager::ReadFuzzyWeight
+===================
+*/
+int idBotFuzzyWeightManager::ReadFuzzyWeight( idParser& source, fuzzyseperator_t* fs )
+{
+ if( source.CheckTokenString( "balance" ) )
+ {
+ fs->type = WT_BALANCE;
+
+ if( !source.ExpectTokenString( "(" ) )
+ {
+ return false;
+ }
+
+ if( !ReadValue( source, &fs->weight ) )
+ {
+ return false;
+ }
+
+ if( !source.ExpectTokenString( "," ) )
+ {
+ return false;
+ }
+
+ if( !ReadValue( source, &fs->minweight ) )
+ {
+ return false;
+ }
+
+ if( !source.ExpectTokenString( "," ) )
+ {
+ return false;
+ }
+
+ if( !ReadValue( source, &fs->maxweight ) )
+ {
+ return false;
+ }
+
+ if( !source.ExpectTokenString( ")" ) )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ fs->type = 0;
+
+ if( !ReadValue( source, &fs->weight ) )
+ {
+ return false;
+ }
+ fs->minweight = fs->weight;
+ fs->maxweight = fs->weight;
+ }
+
+ if( !source.ExpectTokenString( ";" ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+===================
+idBotFuzzyWeightManager::FreeFuzzySeperators_r
+===================
+*/
+void idBotFuzzyWeightManager::FreeFuzzySeperators_r( fuzzyseperator_t* fs )
+{
+ if( !fs )
+ {
+ return;
+ }
+ if( fs->child )
+ {
+ FreeFuzzySeperators_r( fs->child );
+ }
+ if( fs->next )
+ {
+ FreeFuzzySeperators_r( fs->next );
+ }
+
+ fs->inUse = false;
+}
+
+/*
+===================
+FreeWeightConfig2
+===================
+*/
+void idBotFuzzyWeightManager::FreeWeightConfig2( weightconfig_t* config )
+{
+ int i;
+
+ for( i = 0; i < config->numweights; i++ )
+ {
+ FreeFuzzySeperators_r( config->weights[i].firstseperator );
+ // jmarshall - todo
+ //if (config->weights[i].name)
+ // FreeMemory(config->weights[i].name);
+ // jmarshall end
+ }
+
+}
+
+/*
+===================
+idBotFuzzyWeightManager::FreeWeightConfig
+===================
+*/
+void idBotFuzzyWeightManager::FreeWeightConfig( weightconfig_t* config )
+{
+ //if (!LibVarGetValue("bot_reloadcharacters"))
+ // return;
+
+ FreeWeightConfig2( config );
+}
+
+/*
+===================
+idBotFuzzyWeightManager::ReadFuzzySeperators_r
+===================
+*/
+fuzzyseperator_t* idBotFuzzyWeightManager::ReadFuzzySeperators_r( idParser& source )
+{
+ int newindent, index, founddefault;
+ bool def;
+ idToken token;
+ fuzzyseperator_t* fs, * lastfs, * firstfs;
+
+ founddefault = false;
+ firstfs = NULL;
+ lastfs = NULL;
+ if( !source.ExpectTokenString( "(" ) )
+ {
+ return NULL;
+ }
+
+ if( !source.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) )
+ {
+ return NULL;
+ }
+
+ index = token.GetIntValue();
+
+ if( !source.ExpectTokenString( ")" ) )
+ {
+ return NULL;
+ }
+
+ if( !source.ExpectTokenString( "{" ) )
+ {
+ return NULL;
+ }
+
+ if( !source.ExpectAnyToken( &token ) )
+ {
+ return NULL;
+ }
+
+ do
+ {
+ def = ( token == "default" ); //!strcmp(token.string, "default");
+ if( def || ( token == "case" ) )
+ {
+ fs = AllocFuzzyWeight();
+ fs->index = index;
+ if( lastfs )
+ {
+ lastfs->next = fs;
+ }
+ else
+ {
+ firstfs = fs;
+ }
+ lastfs = fs;
+ if( def )
+ {
+ if( founddefault )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ gameLocal.Error( "switch already has a default\n" );
+ return NULL;
+ }
+ fs->value = MAX_INVENTORYVALUE;
+ founddefault = true;
+ }
+ else
+ {
+ if( !source.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+
+ fs->value = token.GetIntValue();
+ }
+
+ if( !source.ExpectTokenString( ":" ) || !source.ExpectAnyToken( &token ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+
+ newindent = false;
+ if( token == "{" )
+ {
+ newindent = true;
+ if( !source.ExpectAnyToken( &token ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+ }
+
+ if( token == "return" )
+ {
+ if( !ReadFuzzyWeight( source, fs ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+ }
+ else if( token == "switch" )
+ {
+ fs->child = ReadFuzzySeperators_r( source );
+ if( !fs->child )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+ }
+ else
+ {
+ gameLocal.Error( "invalid name %s\n", token.c_str() );
+ return NULL;
+ }
+
+ if( newindent )
+ {
+ if( !source.ExpectTokenString( "}" ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ gameLocal.Error( "invalid name %s\n", token.c_str() );
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+
+ if( !source.ExpectAnyToken( &token ) )
+ {
+ FreeFuzzySeperators_r( firstfs );
+ return NULL;
+ }
+ }
+ while( token != "}" );
+
+ if( !founddefault )
+ {
+ source.Warning( "switch without default\n" );
+ fs = AllocFuzzyWeight();
+ fs->index = index;
+ fs->value = MAX_INVENTORYVALUE;
+ fs->weight = 0;
+ fs->next = NULL;
+ fs->child = NULL;
+ if( lastfs )
+ {
+ lastfs->next = fs;
+ }
+ else
+ {
+ firstfs = fs;
+ }
+ lastfs = fs;
+ }
+
+ return firstfs;
+}
+
+/*
+=====================
+idBotFuzzyWeightManager::ReadWeightConfig
+=====================
+*/
+weightconfig_t* idBotFuzzyWeightManager::ReadWeightConfig( char* filename )
+{
+ int newindent, avail = 0, n;
+ idToken token;
+ idParser source;
+ fuzzyseperator_t* fs;
+ weightconfig_t* config = NULL;
+
+ avail = -1;
+ for( n = 0; n < MAX_WEIGHT_FILES; n++ )
+ {
+ config = &weightFileList[n];
+
+ if( config->inUse )
+ {
+ continue;
+ }
+
+ if( config->filename == filename )
+ {
+ return config;
+ }
+
+ avail = n;
+ break;
+ }
+
+ if( avail == -1 )
+ {
+ gameLocal.Error( "weightFileList was full trying to load %s\n", filename );
+ return NULL;
+ }
+
+ rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
+
+ if( !source.LoadFile( filename ) )
+ {
+ gameLocal.Error( "couldn't load %s\n", filename );
+ return NULL;
+ }
+
+ config = &weightFileList[avail];
+ config->Reset();
+
+ config->filename = filename;
+
+ //parse the item config file
+ while( source.ReadToken( &token ) )
+ {
+ if( token == "weight" )
+ {
+ if( config->numweights >= MAX_WEIGHTS )
+ {
+ gameLocal.Error( "too many fuzzy weights\n" );
+ break;
+ }
+
+ if( !source.ExpectTokenType( TT_STRING, 0, &token ) )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+
+ token.StripDoubleQuotes();
+ config->weights[config->numweights].name = token.c_str();
+
+ if( !source.ExpectAnyToken( &token ) )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+
+ newindent = false;
+ if( token == "{" )
+ {
+ newindent = true;
+ if( !source.ExpectAnyToken( &token ) )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+ }
+
+ if( token == "switch" )
+ {
+ fs = ReadFuzzySeperators_r( source );
+ if( !fs )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+
+ config->weights[config->numweights].firstseperator = fs;
+ }
+ else if( token == "return" )
+ {
+ fs = ( fuzzyseperator_t* )AllocFuzzyWeight();
+ fs->index = 0;
+ fs->value = MAX_INVENTORYVALUE;
+ fs->next = NULL;
+ fs->child = NULL;
+ if( !ReadFuzzyWeight( source, fs ) )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+ config->weights[config->numweights].firstseperator = fs;
+ }
+ else
+ {
+ gameLocal.Error( "invalid name %s\n", token.c_str() );
+ FreeWeightConfig( config );
+ return NULL;
+ }
+
+ if( newindent )
+ {
+ if( !source.ExpectTokenString( "}" ) )
+ {
+ FreeWeightConfig( config );
+ return NULL;
+ }
+ }
+ config->numweights++;
+ }
+ else
+ {
+ gameLocal.Error( "invalid name %s\n", token.c_str() );
+ FreeWeightConfig( config );
+ return NULL;
+ }
+ }
+
+ //if the file was located in a pak file
+ common->Printf( "idBotFuzzyWeightManager::ReadWeightConfig: loaded %s\n", filename );
+ config->inUse = true;
+ return config;
+}
+
+/*
+==================
+idBotFuzzyWeightManager::FindFuzzyWeight
+==================
+*/
+int idBotFuzzyWeightManager::FindFuzzyWeight( weightconfig_t* wc, char* name )
+{
+ int i;
+
+ for( i = 0; i < wc->numweights; i++ )
+ {
+ if( wc->weights[i].name == name )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+==================
+idBotFuzzyWeightManager::FuzzyWeight_r
+==================
+*/
+float idBotFuzzyWeightManager::FuzzyWeight_r( int* inventory, fuzzyseperator_t* fs )
+{
+ float scale, w1, w2;
+
+ if( inventory[fs->index] < fs->value )
+ {
+ if( fs->child )
+ {
+ return FuzzyWeight_r( inventory, fs->child );
+ }
+ else
+ {
+ return fs->weight;
+ }
+ }
+ else if( fs->next )
+ {
+ if( inventory[fs->index] < fs->next->value )
+ {
+ //first weight
+ if( fs->child )
+ {
+ w1 = FuzzyWeight_r( inventory, fs->child );
+ }
+ else
+ {
+ w1 = fs->weight;
+ }
+
+ //second weight
+ if( fs->next->child )
+ {
+ w2 = FuzzyWeight_r( inventory, fs->next->child );
+ }
+ else
+ {
+ w2 = fs->next->weight;
+ }
+
+ //the scale factor
+ scale = ( inventory[fs->index] - fs->value ) / ( fs->next->value - fs->value );
+
+ //scale between the two weights
+ return scale * w1 + ( 1 - scale ) * w2;
+ }
+ return FuzzyWeight_r( inventory, fs->next );
+ }
+ return fs->weight;
+}
+
+/*
+============================
+idBotFuzzyWeightManager::FuzzyWeightUndecided_r
+============================
+*/
+float idBotFuzzyWeightManager::FuzzyWeightUndecided_r( int* inventory, fuzzyseperator_t* fs )
+{
+ float scale, w1, w2;
+
+ if( inventory[fs->index] < fs->value )
+ {
+ if( fs->child )
+ {
+ return FuzzyWeightUndecided_r( inventory, fs->child );
+ }
+ else
+ {
+ return fs->minweight + rvmBotUtil::random() * ( fs->maxweight - fs->minweight );
+ }
+ }
+ else if( fs->next )
+ {
+ if( inventory[fs->index] < fs->next->value )
+ {
+ //first weight
+ if( fs->child )
+ {
+ w1 = FuzzyWeightUndecided_r( inventory, fs->child );
+ }
+ else
+ {
+ w1 = fs->minweight + rvmBotUtil::random() * ( fs->maxweight - fs->minweight );
+ }
+
+ //second weight
+ if( fs->next->child )
+ {
+ w2 = FuzzyWeight_r( inventory, fs->next->child );
+ }
+ else
+ {
+ w2 = fs->next->minweight + rvmBotUtil::random() * ( fs->next->maxweight - fs->next->minweight );
+ }
+
+ //the scale factor
+ scale = ( inventory[fs->index] - fs->value ) / ( fs->next->value - fs->value );
+
+ //scale between the two weights
+ return scale * w1 + ( 1 - scale ) * w2;
+ }
+ return FuzzyWeightUndecided_r( inventory, fs->next );
+ }
+ return fs->weight;
+}
+
+/*
+=================
+idBotFuzzyWeightManager::FuzzyWeight
+=================
+*/
+float idBotFuzzyWeightManager::FuzzyWeight( int* inventory, weightconfig_t* wc, int weightnum )
+{
+ return FuzzyWeight_r( inventory, wc->weights[weightnum].firstseperator );
+}
+
+/*
+=================
+idBotFuzzyWeightManager::FuzzyWeightUndecided
+=================
+*/
+float idBotFuzzyWeightManager::FuzzyWeightUndecided( int* inventory, weightconfig_t* wc, int weightnum )
+{
+ return FuzzyWeightUndecided_r( inventory, wc->weights[weightnum].firstseperator );
+}
+
+/*
+====================
+idBotFuzzyWeightManager::EvolveFuzzySeperator_r
+====================
+*/
+void idBotFuzzyWeightManager::EvolveFuzzySeperator_r( fuzzyseperator_t* fs )
+{
+ if( fs->child )
+ {
+ EvolveFuzzySeperator_r( fs->child );
+ }
+ else if( fs->type == WT_BALANCE )
+ {
+ //every once in a while an evolution leap occurs, mutation
+ if( rvmBotUtil::random() < 0.01 )
+ {
+ fs->weight += rvmBotUtil::crandom() * ( fs->maxweight - fs->minweight );
+ }
+ else
+ {
+ fs->weight += rvmBotUtil::crandom() * ( fs->maxweight - fs->minweight ) * 0.5;
+ }
+
+ //modify bounds if necesary because of mutation
+ if( fs->weight < fs->minweight )
+ {
+ fs->minweight = fs->weight;
+ }
+ else if( fs->weight > fs->maxweight )
+ {
+ fs->maxweight = fs->weight;
+ }
+ }
+ if( fs->next )
+ {
+ EvolveFuzzySeperator_r( fs->next );
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::EvolveWeightConfig
+====================
+*/
+void idBotFuzzyWeightManager::EvolveWeightConfig( weightconfig_t* config )
+{
+ int i;
+
+ for( i = 0; i < config->numweights; i++ )
+ {
+ EvolveFuzzySeperator_r( config->weights[i].firstseperator );
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::ScaleWeight
+====================
+*/
+void idBotFuzzyWeightManager::ScaleFuzzySeperator_r( fuzzyseperator_t* fs, float scale )
+{
+ if( fs->child )
+ {
+ ScaleFuzzySeperator_r( fs->child, scale );
+ }
+ else if( fs->type == WT_BALANCE )
+ {
+ fs->weight = ( fs->maxweight + fs->minweight ) * scale;
+
+ //get the weight between bounds
+ if( fs->weight < fs->minweight )
+ {
+ fs->weight = fs->minweight;
+ }
+ else if( fs->weight > fs->maxweight )
+ {
+ fs->weight = fs->maxweight;
+ }
+ }
+ if( fs->next )
+ {
+ ScaleFuzzySeperator_r( fs->next, scale );
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::ScaleWeight
+====================
+*/
+void idBotFuzzyWeightManager::ScaleWeight( weightconfig_t* config, char* name, float scale )
+{
+ int i;
+
+ if( scale < 0 )
+ {
+ scale = 0;
+ }
+ else if( scale > 1 )
+ {
+ scale = 1;
+ }
+ for( i = 0; i < config->numweights; i++ )
+ {
+ if( config->weights[i].name == name )
+ {
+ ScaleFuzzySeperator_r( config->weights[i].firstseperator, scale );
+ break;
+ }
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::ScaleFuzzySeperatorBalanceRange_r
+====================
+*/
+void idBotFuzzyWeightManager::ScaleFuzzySeperatorBalanceRange_r( fuzzyseperator_t* fs, float scale )
+{
+ if( fs->child )
+ {
+ ScaleFuzzySeperatorBalanceRange_r( fs->child, scale );
+ }
+ else if( fs->type == WT_BALANCE )
+ {
+ float mid = ( fs->minweight + fs->maxweight ) * 0.5;
+ //get the weight between bounds
+ fs->maxweight = mid + ( fs->maxweight - mid ) * scale;
+ fs->minweight = mid + ( fs->minweight - mid ) * scale;
+ if( fs->maxweight < fs->minweight )
+ {
+ fs->maxweight = fs->minweight;
+ }
+ }
+ if( fs->next )
+ {
+ ScaleFuzzySeperatorBalanceRange_r( fs->next, scale );
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::ScaleFuzzyBalanceRange
+====================
+*/
+void idBotFuzzyWeightManager::ScaleFuzzyBalanceRange( weightconfig_t* config, float scale )
+{
+ int i;
+
+ if( scale < 0 )
+ {
+ scale = 0;
+ }
+ else if( scale > 100 )
+ {
+ scale = 100;
+ }
+ for( i = 0; i < config->numweights; i++ )
+ {
+ ScaleFuzzySeperatorBalanceRange_r( config->weights[i].firstseperator, scale );
+ }
+}
+
+/*
+====================
+idBotFuzzyWeightManager::InterbreedFuzzySeperator_r
+====================
+*/
+int idBotFuzzyWeightManager::InterbreedFuzzySeperator_r( fuzzyseperator_t* fs1, fuzzyseperator_t* fs2, fuzzyseperator_t* fsout )
+{
+ if( fs1->child )
+ {
+ if( !fs2->child || !fsout->child )
+ {
+ gameLocal.Error( "cannot interbreed weight configs, unequal child\n" );
+ return false;
+ }
+ if( !InterbreedFuzzySeperator_r( fs2->child, fs2->child, fsout->child ) )
+ {
+ return false;
+ }
+ }
+ else if( fs1->type == WT_BALANCE )
+ {
+ if( fs2->type != WT_BALANCE || fsout->type != WT_BALANCE )
+ {
+ gameLocal.Error( "cannot interbreed weight configs, unequal balance\n" );
+ return false;
+ }
+ fsout->weight = ( fs1->weight + fs2->weight ) / 2;
+ if( fsout->weight > fsout->maxweight )
+ {
+ fsout->maxweight = fsout->weight;
+ }
+ if( fsout->weight > fsout->minweight )
+ {
+ fsout->minweight = fsout->weight;
+ }
+ }
+ if( fs1->next )
+ {
+ if( !fs2->next || !fsout->next )
+ {
+ gameLocal.Error( "cannot interbreed weight configs, unequal next\n" );
+ return false;
+ }
+ if( !InterbreedFuzzySeperator_r( fs1->next, fs2->next, fsout->next ) )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+====================
+idBotFuzzyWeightManager::InterbreedWeightConfigs
+====================
+*/
+void idBotFuzzyWeightManager::InterbreedWeightConfigs( weightconfig_t* config1, weightconfig_t* config2, weightconfig_t* configout )
+{
+ int i;
+
+ if( config1->numweights != config2->numweights ||
+ config1->numweights != configout->numweights )
+ {
+ gameLocal.Error( "cannot interbreed weight configs, unequal numweights\n" );
+ return;
+ }
+
+ for( i = 0; i < config1->numweights; i++ )
+ {
+ InterbreedFuzzySeperator_r( config1->weights[i].firstseperator,
+ config2->weights[i].firstseperator,
+ configout->weights[i].firstseperator );
+ }
+}
+
+
+/*
+=======================
+idBotFuzzyWeightManager::BotShutdownWeights
+=======================
+*/
+void idBotFuzzyWeightManager::BotShutdownWeights( void )
+{
+ int i;
+
+ for( i = 0; i < MAX_WEIGHT_FILES; i++ )
+ {
+ weightFileList[i].inUse = false;
+ //if (weightFileList[i])
+ //{
+ // FreeWeightConfig2(weightFileList[i]);
+ // weightFileList[i] = NULL;
+ //}
+ }
+}
\ No newline at end of file
diff --git a/neo/doom3/game/bots/Bot_weights.h b/neo/doom3/game/bots/Bot_weights.h
new file mode 100644
index 00000000..20a55029
--- /dev/null
+++ b/neo/doom3/game/bots/Bot_weights.h
@@ -0,0 +1,53 @@
+// Bot_weights.h
+//
+
+#define MAX_INVENTORYVALUE 999999
+#define EVALUATERECURSIVELY
+
+#define MAX_WEIGHT_FILES 128
+#define MAX_FUZZY_OPERATORS 8192
+
+class idBotFuzzyWeightManager
+{
+public:
+ // Init the fuzzy weight manager.
+ void Init( void );
+
+ // Parses a weight config file.
+ weightconfig_t* ReadWeightConfig( char* filename );
+
+ void BotShutdownWeights( void );
+
+ // Fuzzy Weight simulation functions.
+ int FindFuzzyWeight( weightconfig_t* wc, char* name );
+ float FuzzyWeight( int* inventory, weightconfig_t* wc, int weightnum );
+ float FuzzyWeightUndecided( int* inventory, weightconfig_t* wc, int weightnum );
+ void EvolveFuzzySeperator_r( fuzzyseperator_t* fs );
+ void EvolveWeightConfig( weightconfig_t* config );
+ void ScaleWeight( weightconfig_t* config, char* name, float scale );
+ void ScaleFuzzyBalanceRange( weightconfig_t* config, float scale );
+ void InterbreedWeightConfigs( weightconfig_t* config1, weightconfig_t* config2, weightconfig_t* configout );
+ void FreeWeightConfig( weightconfig_t* config );
+private:
+ fuzzyseperator_t* AllocFuzzyWeight( void );
+ bool ReadValue( idParser& source, float* value );
+ int ReadFuzzyWeight( idParser& source, fuzzyseperator_t* fs );
+
+ int InterbreedFuzzySeperator_r( fuzzyseperator_t* fs1, fuzzyseperator_t* fs2, fuzzyseperator_t* fsout );
+
+ fuzzyseperator_t* ReadFuzzySeperators_r( idParser& source );
+
+ void FreeWeightConfig2( weightconfig_t* config );
+ void FreeFuzzySeperators_r( fuzzyseperator_t* fs );
+
+ void ScaleFuzzySeperatorBalanceRange_r( fuzzyseperator_t* fs, float scale );
+
+ void ScaleFuzzySeperator_r( fuzzyseperator_t* fs, float scale );
+ float FuzzyWeight_r( int* inventory, fuzzyseperator_t* fs );
+ float FuzzyWeightUndecided_r( int* inventory, fuzzyseperator_t* fs );
+
+ weightconfig_t weightFileList[MAX_WEIGHT_FILES];
+ fuzzyseperator_t fuzzyseperators[MAX_FUZZY_OPERATORS];
+};
+
+extern idBotFuzzyWeightManager botFuzzyWeightManager;
\ No newline at end of file
diff --git a/neo/doom3/game/gamesys/GameTypeInfo.cpp b/neo/doom3/game/gamesys/GameTypeInfo.cpp
index f4b725fd..8c2b30d2 100644
--- a/neo/doom3/game/gamesys/GameTypeInfo.cpp
+++ b/neo/doom3/game/gamesys/GameTypeInfo.cpp
@@ -3513,6 +3513,63 @@ bool idDebris::HasNativeFunction(const char *functionName) {
};
+intptr_t rvmWeaponObject::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 88310) { // Execute
+ Execute();
+ return 0;
+ };
+ if(functionNameHash == 113919) { // IsRunning
+ return (intptr_t)IsRunning();
+ };
+ if(functionNameHash == 110318) { // OwnerDied
+ OwnerDied();
+ return 0;
+ };
+ if(functionNameHash == 97537) { // IsFiring
+ return (intptr_t)IsFiring();
+ };
+ if(functionNameHash == 137206) { // IsReloading
+ return (intptr_t)IsReloading();
+ };
+ if(functionNameHash == 115454) { // Holstered
+ return (intptr_t)Holstered((stateParms_t *)param1);
+ };
+ if(functionNameHash == 139817) { // IsHolstered
+ IsHolstered();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponObject::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 88310) { // Execute
+ return true;
+ };
+ if(functionNameHash == 113919) { // IsRunning
+ return true;
+ };
+ if(functionNameHash == 110318) { // OwnerDied
+ return true;
+ };
+ if(functionNameHash == 97537) { // IsFiring
+ return true;
+ };
+ if(functionNameHash == 137206) { // IsReloading
+ return true;
+ };
+ if(functionNameHash == 115454) { // Holstered
+ return true;
+ };
+ if(functionNameHash == 139817) { // IsHolstered
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
int functionNameHash = idStr::Hash(functionName);
if(functionNameHash == 63102) { // Spawn
@@ -3526,13 +3583,13 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
ShouldConstructScriptObjectAtSpawn();
return 0;
};
+ if(functionNameHash == 136930) { // InvokeChild
+ return (intptr_t)InvokeChild();
+ };
if(functionNameHash == 59010) { // Clear
Clear();
return 0;
};
- if(functionNameHash == 96516) { // IsLinked
- return (intptr_t)IsLinked();
- };
if(functionNameHash == 216848) { // IsWorldModelReady
return (intptr_t)IsWorldModelReady();
};
@@ -3543,9 +3600,21 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
UpdateGUI();
return 0;
};
+ if(functionNameHash == 81854) { // PdaIcon
+ return (intptr_t)PdaIcon();
+ };
+ if(functionNameHash == 137836) { // DisplayName
+ return (intptr_t)DisplayName();
+ };
+ if(functionNameHash == 143596) { // Description
+ return (intptr_t)Description();
+ };
if(functionNameHash == 125553) { // UpdateSkin
return (intptr_t)UpdateSkin();
};
+ if(functionNameHash == 97537) { // IsFiring
+ return (intptr_t)IsFiring();
+ };
if(functionNameHash == 61762) { // Think
Think();
return 0;
@@ -3617,32 +3686,21 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
WeaponStolen();
return 0;
};
- if(functionNameHash == 282905) { // ConstructScriptObject
- return (intptr_t)ConstructScriptObject();
- };
- if(functionNameHash == 311375) { // DeconstructScriptObject
- DeconstructScriptObject();
- return 0;
- };
- if(functionNameHash == 154576) { // UpdateScript
- UpdateScript();
- return 0;
- };
- if(functionNameHash == 178279) { // EnterCinematic
- EnterCinematic();
- return 0;
- };
- if(functionNameHash == 165047) { // ExitCinematic
- ExitCinematic();
- return 0;
- };
- if(functionNameHash == 124601) { // NetCatchup
- NetCatchup();
+ if(functionNameHash == 184609) { // ForceAmmoInClip
+ ForceAmmoInClip();
return 0;
};
if(functionNameHash == 124648) { // GetZoomFov
return (intptr_t)GetZoomFov();
};
+ if(functionNameHash == 151891) { // FlashlightOn
+ FlashlightOn();
+ return 0;
+ };
+ if(functionNameHash == 164213) { // FlashlightOff
+ FlashlightOff();
+ return 0;
+ };
if(functionNameHash == 136649) { // GetAmmoType
GetAmmoType();
return 0;
@@ -3666,25 +3724,29 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
if(functionNameHash == 152979) { // AmmoRequired
return (intptr_t)AmmoRequired();
};
+ if(functionNameHash == 112768) { // AmmoCount
+ return (intptr_t)AmmoCount();
+ };
+ if(functionNameHash == 188573) { // GetGrabberState
+ return (intptr_t)GetGrabberState();
+ };
+ if(functionNameHash == 163273) { // GetWorldModel
+ return (intptr_t)GetWorldModel();
+ };
if(functionNameHash == 278761) { // ClientPredictionThink
ClientPredictionThink();
return 0;
};
- if(functionNameHash == 170742) { // AlertMonsters
- AlertMonsters();
- return 0;
- };
if(functionNameHash == 208077) { // MuzzleFlashLight
MuzzleFlashLight();
return 0;
};
- if(functionNameHash == 181199) { // UpdateNozzleFx
- UpdateNozzleFx();
+ if(functionNameHash == 297946) { // RemoveMuzzleFlashlight
+ RemoveMuzzleFlashlight();
return 0;
};
- if(functionNameHash == 251065) { // UpdateFlashPosition
- UpdateFlashPosition();
- return 0;
+ if(functionNameHash == 199165) { // GetDeclEntityDef
+ return (intptr_t)GetDeclEntityDef();
};
if(functionNameHash == 135992) { // Event_Clear
Event_Clear();
@@ -3722,6 +3784,9 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
Event_AmmoInClip();
return 0;
};
+ if(functionNameHash == 161573) { // AmmoAvailable
+ return (intptr_t)AmmoAvailable();
+ };
if(functionNameHash == 243379) { // Event_AmmoAvailable
Event_AmmoAvailable();
return 0;
@@ -3738,6 +3803,9 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
Event_Next();
return 0;
};
+ if(functionNameHash == 207382) { // CreateProjectile
+ return (intptr_t)CreateProjectile();
+ };
if(functionNameHash == 291264) { // Event_CreateProjectile
Event_CreateProjectile();
return 0;
@@ -3763,13 +3831,50 @@ intptr_t idWeapon::Invoke(const char *functionName, void *param1) {
return 0;
};
if(functionNameHash == 219964) { // Event_IsInvisible
- Event_IsInvisible();
- return 0;
+ return (intptr_t)Event_IsInvisible();
};
if(functionNameHash == 227297) { // Event_NetEndReload
Event_NetEndReload();
return 0;
};
+ if(functionNameHash == 178279) { // EnterCinematic
+ EnterCinematic();
+ return 0;
+ };
+ if(functionNameHash == 165047) { // ExitCinematic
+ ExitCinematic();
+ return 0;
+ };
+ if(functionNameHash == 124601) { // NetCatchup
+ NetCatchup();
+ return 0;
+ };
+ if(functionNameHash == 96516) { // IsLinked
+ return (intptr_t)IsLinked();
+ };
+ if(functionNameHash == 170742) { // AlertMonsters
+ AlertMonsters();
+ return 0;
+ };
+ if(functionNameHash == 181199) { // UpdateNozzleFx
+ UpdateNozzleFx();
+ return 0;
+ };
+ if(functionNameHash == 251065) { // UpdateFlashPosition
+ UpdateFlashPosition();
+ return 0;
+ };
+ if(functionNameHash == 285326) { // Event_GrabberHasTarget
+ return (intptr_t)Event_GrabberHasTarget();
+ };
+ if(functionNameHash == 293443) { // Event_StartWeaponSmoke
+ Event_StartWeaponSmoke();
+ return 0;
+ };
+ if(functionNameHash == 278872) { // Event_StopWeaponSmoke
+ Event_StopWeaponSmoke();
+ return 0;
+ };
return __super::Invoke(functionName, param1);
};
@@ -3785,10 +3890,10 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 476562) { // ShouldConstructScriptObjectAtSpawn
return true;
};
- if(functionNameHash == 59010) { // Clear
+ if(functionNameHash == 136930) { // InvokeChild
return true;
};
- if(functionNameHash == 96516) { // IsLinked
+ if(functionNameHash == 59010) { // Clear
return true;
};
if(functionNameHash == 216848) { // IsWorldModelReady
@@ -3800,9 +3905,21 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 103137) { // UpdateGUI
return true;
};
+ if(functionNameHash == 81854) { // PdaIcon
+ return true;
+ };
+ if(functionNameHash == 137836) { // DisplayName
+ return true;
+ };
+ if(functionNameHash == 143596) { // Description
+ return true;
+ };
if(functionNameHash == 125553) { // UpdateSkin
return true;
};
+ if(functionNameHash == 97537) { // IsFiring
+ return true;
+ };
if(functionNameHash == 61762) { // Think
return true;
};
@@ -3860,27 +3977,18 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 155408) { // WeaponStolen
return true;
};
- if(functionNameHash == 282905) { // ConstructScriptObject
- return true;
- };
- if(functionNameHash == 311375) { // DeconstructScriptObject
- return true;
- };
- if(functionNameHash == 154576) { // UpdateScript
- return true;
- };
- if(functionNameHash == 178279) { // EnterCinematic
- return true;
- };
- if(functionNameHash == 165047) { // ExitCinematic
- return true;
- };
- if(functionNameHash == 124601) { // NetCatchup
+ if(functionNameHash == 184609) { // ForceAmmoInClip
return true;
};
if(functionNameHash == 124648) { // GetZoomFov
return true;
};
+ if(functionNameHash == 151891) { // FlashlightOn
+ return true;
+ };
+ if(functionNameHash == 164213) { // FlashlightOff
+ return true;
+ };
if(functionNameHash == 136649) { // GetAmmoType
return true;
};
@@ -3902,19 +4010,25 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 152979) { // AmmoRequired
return true;
};
- if(functionNameHash == 278761) { // ClientPredictionThink
+ if(functionNameHash == 112768) { // AmmoCount
return true;
};
- if(functionNameHash == 170742) { // AlertMonsters
+ if(functionNameHash == 188573) { // GetGrabberState
+ return true;
+ };
+ if(functionNameHash == 163273) { // GetWorldModel
+ return true;
+ };
+ if(functionNameHash == 278761) { // ClientPredictionThink
return true;
};
if(functionNameHash == 208077) { // MuzzleFlashLight
return true;
};
- if(functionNameHash == 181199) { // UpdateNozzleFx
+ if(functionNameHash == 297946) { // RemoveMuzzleFlashlight
return true;
};
- if(functionNameHash == 251065) { // UpdateFlashPosition
+ if(functionNameHash == 199165) { // GetDeclEntityDef
return true;
};
if(functionNameHash == 135992) { // Event_Clear
@@ -3944,6 +4058,9 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 199693) { // Event_AmmoInClip
return true;
};
+ if(functionNameHash == 161573) { // AmmoAvailable
+ return true;
+ };
if(functionNameHash == 243379) { // Event_AmmoAvailable
return true;
};
@@ -3956,6 +4073,9 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 126624) { // Event_Next
return true;
};
+ if(functionNameHash == 207382) { // CreateProjectile
+ return true;
+ };
if(functionNameHash == 291264) { // Event_CreateProjectile
return true;
};
@@ -3980,6 +4100,36 @@ bool idWeapon::HasNativeFunction(const char *functionName) {
if(functionNameHash == 227297) { // Event_NetEndReload
return true;
};
+ if(functionNameHash == 178279) { // EnterCinematic
+ return true;
+ };
+ if(functionNameHash == 165047) { // ExitCinematic
+ return true;
+ };
+ if(functionNameHash == 124601) { // NetCatchup
+ return true;
+ };
+ if(functionNameHash == 96516) { // IsLinked
+ return true;
+ };
+ if(functionNameHash == 170742) { // AlertMonsters
+ return true;
+ };
+ if(functionNameHash == 181199) { // UpdateNozzleFx
+ return true;
+ };
+ if(functionNameHash == 251065) { // UpdateFlashPosition
+ return true;
+ };
+ if(functionNameHash == 285326) { // Event_GrabberHasTarget
+ return true;
+ };
+ if(functionNameHash == 293443) { // Event_StartWeaponSmoke
+ return true;
+ };
+ if(functionNameHash == 278872) { // Event_StopWeaponSmoke
+ return true;
+ };
return __super::HasNativeFunction(functionName);
};
@@ -8417,3 +8567,723 @@ bool idThread::HasNativeFunction(const char *functionName) {
};
+intptr_t rvmWeaponFist::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 132482) { // GetFireAnim
+ return (intptr_t)GetFireAnim();
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponFist::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 132482) { // GetFireAnim
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponPistol::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponPistol::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponFlashlight::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ UpdateSkin();
+ return 0;
+ };
+ if(functionNameHash == 268148) { // UpdateLightIntensity
+ UpdateLightIntensity();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponFlashlight::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ return true;
+ };
+ if(functionNameHash == 268148) { // UpdateLightIntensity
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponPDA::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponPDA::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponShotgun::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponShotgun::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponDoubleShotgun::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 123479) { // EjectBrass
+ EjectBrass();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponDoubleShotgun::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 123479) { // EjectBrass
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponMachineGun::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponMachineGun::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponPlasmaGun::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponPlasmaGun::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponChainGun::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 150892) { // UpdateBarrel
+ UpdateBarrel();
+ return 0;
+ };
+ if(functionNameHash == 73785) { // SpinUp
+ SpinUp();
+ return 0;
+ };
+ if(functionNameHash == 100305) { // SpinDown
+ SpinDown();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponChainGun::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 150892) { // UpdateBarrel
+ return true;
+ };
+ if(functionNameHash == 73785) { // SpinUp
+ return true;
+ };
+ if(functionNameHash == 100305) { // SpinDown
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponRocketLauncher::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ UpdateSkin();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponRocketLauncher::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponBFG::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 123325) { // OverCharge
+ OverCharge();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponBFG::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 123325) { // OverCharge
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponHandgrenade::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 110318) { // OwnerDied
+ OwnerDied();
+ return 0;
+ };
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 132702) { // GrenadeNade
+ GrenadeNade();
+ return 0;
+ };
+ if(functionNameHash == 157379) { // GrenadeNoNade
+ GrenadeNoNade();
+ return 0;
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ UpdateSkin();
+ return 0;
+ };
+ if(functionNameHash == 160314) { // ExplodeInHand
+ ExplodeInHand();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponHandgrenade::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 110318) { // OwnerDied
+ return true;
+ };
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 132702) { // GrenadeNade
+ return true;
+ };
+ if(functionNameHash == 157379) { // GrenadeNoNade
+ return true;
+ };
+ if(functionNameHash == 125553) { // UpdateSkin
+ return true;
+ };
+ if(functionNameHash == 160314) { // ExplodeInHand
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponChainsaw::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponChainsaw::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
+intptr_t rvmWeaponGrabber::Invoke(const char *functionName, void *param1) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return (intptr_t)Raise((stateParms_t *)param1);
+ };
+ if(functionNameHash == 63107) { // Lower
+ return (intptr_t)Lower((stateParms_t *)param1);
+ };
+ if(functionNameHash == 46077) { // Idle
+ return (intptr_t)Idle((stateParms_t *)param1);
+ };
+ if(functionNameHash == 47046) { // Fire
+ return (intptr_t)Fire((stateParms_t *)param1);
+ };
+ if(functionNameHash == 72819) { // Reload
+ return (intptr_t)Reload((stateParms_t *)param1);
+ };
+ if(functionNameHash == 140229) { // StartActive
+ StartActive();
+ return 0;
+ };
+ if(functionNameHash == 126807) { // StopActive
+ StopActive();
+ return 0;
+ };
+ if(functionNameHash == 225303) { // StartWarningSound
+ StartWarningSound();
+ return 0;
+ };
+ if(functionNameHash == 237121) { // UpdateWarningSound
+ UpdateWarningSound();
+ return 0;
+ };
+ if(functionNameHash == 176832) { // UpdateGuiLight
+ UpdateGuiLight();
+ return 0;
+ };
+ return __super::Invoke(functionName, param1);
+
+};
+
+bool rvmWeaponGrabber::HasNativeFunction(const char *functionName) {
+ int functionNameHash = idStr::Hash(functionName);
+ if(functionNameHash == 60556) { // Raise
+ return true;
+ };
+ if(functionNameHash == 63107) { // Lower
+ return true;
+ };
+ if(functionNameHash == 46077) { // Idle
+ return true;
+ };
+ if(functionNameHash == 47046) { // Fire
+ return true;
+ };
+ if(functionNameHash == 72819) { // Reload
+ return true;
+ };
+ if(functionNameHash == 140229) { // StartActive
+ return true;
+ };
+ if(functionNameHash == 126807) { // StopActive
+ return true;
+ };
+ if(functionNameHash == 225303) { // StartWarningSound
+ return true;
+ };
+ if(functionNameHash == 237121) { // UpdateWarningSound
+ return true;
+ };
+ if(functionNameHash == 176832) { // UpdateGuiLight
+ return true;
+ };
+ return __super::HasNativeFunction(functionName);
+
+};
+
diff --git a/neo/doom3/game/gamesys/GameTypeInfo.h b/neo/doom3/game/gamesys/GameTypeInfo.h
index ac060766..b4a6dab9 100644
--- a/neo/doom3/game/gamesys/GameTypeInfo.h
+++ b/neo/doom3/game/gamesys/GameTypeInfo.h
@@ -7,10 +7,10 @@
This file has been generated with the Type Info Generator v1.1 (c) 2004 id Software
- 1178 constants
- 131 enums
- 536 classes/structs/unions
- 37 templates
+ 1172 constants
+ 130 enums
+ 556 classes/structs/unions
+ 38 templates
7 max inheritance level for 'idAI_Vagary'
===================================================================================
@@ -952,12 +952,6 @@ static constantInfo_t constantInfo[] = {
{ "int", "idProjectile::LAUNCHED", "2" },
{ "int", "idProjectile::FIZZLED", "3" },
{ "int", "idProjectile::EXPLODED", "4" },
- { "int", "WP_READY", "0" },
- { "int", "WP_OUTOFAMMO", "1" },
- { "int", "WP_RELOAD", "2" },
- { "int", "WP_HOLSTERED", "3" },
- { "int", "WP_RISING", "4" },
- { "int", "WP_LOWERING", "5" },
{ "static const int", "AMMO_NUMTYPES", "16" },
{ "static const int", "LIGHTID_WORLD_MUZZLE_FLASH", "1" },
{ "static const int", "LIGHTID_VIEW_MUZZLE_FLASH", "100" },
@@ -2381,17 +2375,7 @@ static enumValueInfo_t idProjectile_projectileState_t_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t weaponStatus_t_typeInfo[] = {
- { "WP_READY", 0 },
- { "WP_OUTOFAMMO", 1 },
- { "WP_RELOAD", 2 },
- { "WP_HOLSTERED", 3 },
- { "WP_RISING", 4 },
- { "WP_LOWERING", 5 },
- { NULL, 0 }
-};
-
-static enumValueInfo_t idWeapon_enum_108_typeInfo[] = {
+static enumValueInfo_t idWeapon_enum_107_typeInfo[] = {
{ "EVENT_RELOAD", 2 },
{ "EVENT_ENDRELOAD", 3 },
{ "EVENT_CHANGESKIN", 4 },
@@ -2399,13 +2383,13 @@ static enumValueInfo_t idWeapon_enum_108_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t idLight_enum_109_typeInfo[] = {
+static enumValueInfo_t idLight_enum_108_typeInfo[] = {
{ "EVENT_BECOMEBROKEN", 2 },
{ "EVENT_MAXEVENTS", 3 },
{ NULL, 0 }
};
-static enumValueInfo_t idItem_enum_110_typeInfo[] = {
+static enumValueInfo_t idItem_enum_109_typeInfo[] = {
{ "EVENT_PICKUP", 2 },
{ "EVENT_RESPAWN", 3 },
{ "EVENT_RESPAWNFX", 4 },
@@ -2420,7 +2404,7 @@ static enumValueInfo_t playerIconType_t_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t enum_112_typeInfo[] = {
+static enumValueInfo_t enum_111_typeInfo[] = {
{ "BERSERK", 0 },
{ "INVISIBILITY", 1 },
{ "MEGAHEALTH", 2 },
@@ -2429,7 +2413,7 @@ static enumValueInfo_t enum_112_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t enum_113_typeInfo[] = {
+static enumValueInfo_t enum_112_typeInfo[] = {
{ "SPEED", 0 },
{ "PROJECTILE_DAMAGE", 1 },
{ "MELEE_DAMAGE", 2 },
@@ -2437,7 +2421,7 @@ static enumValueInfo_t enum_113_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t enum_114_typeInfo[] = {
+static enumValueInfo_t enum_113_typeInfo[] = {
{ "INFLUENCE_NONE", 0 },
{ "INFLUENCE_LEVEL1", 1 },
{ "INFLUENCE_LEVEL2", 2 },
@@ -2445,7 +2429,7 @@ static enumValueInfo_t enum_114_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t idPlayer_enum_115_typeInfo[] = {
+static enumValueInfo_t idPlayer_enum_114_typeInfo[] = {
{ "EVENT_IMPULSE", 2 },
{ "EVENT_EXIT_TELEPORTER", 3 },
{ "EVENT_ABORT_TELEPORTER", 4 },
@@ -2502,7 +2486,7 @@ static enumValueInfo_t moverState_t_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t idExplodingBarrel_enum_121_typeInfo[] = {
+static enumValueInfo_t idExplodingBarrel_enum_120_typeInfo[] = {
{ "EVENT_EXPLODE", 2 },
{ "EVENT_MAXEVENTS", 3 },
{ NULL, 0 }
@@ -2516,7 +2500,7 @@ static enumValueInfo_t idExplodingBarrel_explode_state_t_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t idSecurityCamera_enum_123_typeInfo[] = {
+static enumValueInfo_t idSecurityCamera_enum_122_typeInfo[] = {
{ "SCANNING", 0 },
{ "LOSINGINTEREST", 1 },
{ "ALERT", 2 },
@@ -2524,7 +2508,7 @@ static enumValueInfo_t idSecurityCamera_enum_123_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t idBrittleFracture_enum_124_typeInfo[] = {
+static enumValueInfo_t idBrittleFracture_enum_123_typeInfo[] = {
{ "EVENT_PROJECT_DECAL", 2 },
{ "EVENT_SHATTER", 3 },
{ "EVENT_MAXEVENTS", 4 },
@@ -2591,7 +2575,7 @@ static enumValueInfo_t stopEvent_t_typeInfo[] = {
{ NULL, 0 }
};
-static enumValueInfo_t enum_130_typeInfo[] = {
+static enumValueInfo_t enum_129_typeInfo[] = {
{ "OP_RETURN", 0 },
{ "OP_UINC_F", 1 },
{ "OP_UINCP_F", 2 },
@@ -2827,30 +2811,29 @@ static enumTypeInfo_t enumTypeInfo[] = {
{ "idPlayerStart::enum_104", idPlayerStart_enum_104_typeInfo },
{ "idProjectile::enum_105", idProjectile_enum_105_typeInfo },
{ "idProjectile::projectileState_t", idProjectile_projectileState_t_typeInfo },
- { "weaponStatus_t", weaponStatus_t_typeInfo },
- { "idWeapon::enum_108", idWeapon_enum_108_typeInfo },
- { "idLight::enum_109", idLight_enum_109_typeInfo },
- { "idItem::enum_110", idItem_enum_110_typeInfo },
+ { "idWeapon::enum_107", idWeapon_enum_107_typeInfo },
+ { "idLight::enum_108", idLight_enum_108_typeInfo },
+ { "idItem::enum_109", idItem_enum_109_typeInfo },
{ "playerIconType_t", playerIconType_t_typeInfo },
+ { "enum_111", enum_111_typeInfo },
{ "enum_112", enum_112_typeInfo },
{ "enum_113", enum_113_typeInfo },
- { "enum_114", enum_114_typeInfo },
- { "idPlayer::enum_115", idPlayer_enum_115_typeInfo },
+ { "idPlayer::enum_114", idPlayer_enum_114_typeInfo },
{ "idMover::moveStage_t", idMover_moveStage_t_typeInfo },
{ "idMover::moverCommand_t", idMover_moverCommand_t_typeInfo },
{ "idMover::moverDir_t", idMover_moverDir_t_typeInfo },
{ "idElevator::elevatorState_t", idElevator_elevatorState_t_typeInfo },
{ "moverState_t", moverState_t_typeInfo },
- { "idExplodingBarrel::enum_121", idExplodingBarrel_enum_121_typeInfo },
+ { "idExplodingBarrel::enum_120", idExplodingBarrel_enum_120_typeInfo },
{ "idExplodingBarrel::explode_state_t", idExplodingBarrel_explode_state_t_typeInfo },
- { "idSecurityCamera::enum_123", idSecurityCamera_enum_123_typeInfo },
- { "idBrittleFracture::enum_124", idBrittleFracture_enum_124_typeInfo },
+ { "idSecurityCamera::enum_122", idSecurityCamera_enum_122_typeInfo },
+ { "idBrittleFracture::enum_123", idBrittleFracture_enum_123_typeInfo },
{ "moveType_t", moveType_t_typeInfo },
{ "moveCommand_t", moveCommand_t_typeInfo },
{ "talkState_t", talkState_t_typeInfo },
{ "moveStatus_t", moveStatus_t_typeInfo },
{ "stopEvent_t", stopEvent_t_typeInfo },
- { "enum_130", enum_130_typeInfo },
+ { "enum_129", enum_129_typeInfo },
{ NULL, NULL }
};
@@ -2945,6 +2928,10 @@ static classVariableInfo_t idMath_typeInfo[] = {
{ NULL, 0 }
};
+static classVariableInfo_t rvRandom_typeInfo[] = {
+ { NULL, 0 }
+};
+
static classVariableInfo_t idRandom_typeInfo[] = {
{ ": int", "seed", (intptr_t)(&((idRandom *)0)->seed), sizeof( ((idRandom *)0)->seed ) },
{ NULL, 0 }
@@ -4001,9 +3988,9 @@ static classVariableInfo_t glRaytracingVec3_t_typeInfo[] = {
{ NULL, 0 }
};
-static classVariableInfo_t glRaytracingLight_s_class_171_typeInfo[] = {
-// { "float", "pad1", (intptr_t)(&((glRaytracingLight_s::class_171 *)0)->pad1), sizeof( ((glRaytracingLight_s::class_171 *)0)->pad1 ) },
-// { "float", "volumetricScattering", (intptr_t)(&((glRaytracingLight_s::class_171 *)0)->volumetricScattering), sizeof( ((glRaytracingLight_s::class_171 *)0)->volumetricScattering ) },
+static classVariableInfo_t glRaytracingLight_s_class_172_typeInfo[] = {
+// { "float", "pad1", (intptr_t)(&((glRaytracingLight_s::class_172 *)0)->pad1), sizeof( ((glRaytracingLight_s::class_172 *)0)->pad1 ) },
+// { "float", "volumetricScattering", (intptr_t)(&((glRaytracingLight_s::class_172 *)0)->volumetricScattering), sizeof( ((glRaytracingLight_s::class_172 *)0)->volumetricScattering ) },
{ NULL, 0 }
};
@@ -5120,11 +5107,11 @@ static classVariableInfo_t frameLookup_t_typeInfo[] = {
{ NULL, 0 }
};
-static classVariableInfo_t class_280_class_280_typeInfo[] = {
-// { "const idSoundShader *", "soundShader", (intptr_t)(&((class_280::class_280 *)0)->soundShader), sizeof( ((class_280::class_280 *)0)->soundShader ) },
-// { "const function_t *", "function", (intptr_t)(&((class_280::class_280 *)0)->function), sizeof( ((class_280::class_280 *)0)->function ) },
-// { "const idDeclSkin *", "skin", (intptr_t)(&((class_280::class_280 *)0)->skin), sizeof( ((class_280::class_280 *)0)->skin ) },
-// { "int", "index", (intptr_t)(&((class_280::class_280 *)0)->index), sizeof( ((class_280::class_280 *)0)->index ) },
+static classVariableInfo_t class_281_class_281_typeInfo[] = {
+// { "const idSoundShader *", "soundShader", (intptr_t)(&((class_281::class_281 *)0)->soundShader), sizeof( ((class_281::class_281 *)0)->soundShader ) },
+// { "const function_t *", "function", (intptr_t)(&((class_281::class_281 *)0)->function), sizeof( ((class_281::class_281 *)0)->function ) },
+// { "const idDeclSkin *", "skin", (intptr_t)(&((class_281::class_281 *)0)->skin), sizeof( ((class_281::class_281 *)0)->skin ) },
+// { "int", "index", (intptr_t)(&((class_281::class_281 *)0)->index), sizeof( ((class_281::class_281 *)0)->index ) },
{ NULL, 0 }
};
@@ -5462,6 +5449,12 @@ static classVariableInfo_t idEventQueue_typeInfo[] = {
{ NULL, 0 }
};
+static classVariableInfo_t rvmGameDelayRemoveEntry_t_typeInfo[] = {
+ { "int32_t", "removeTime", (intptr_t)(&((rvmGameDelayRemoveEntry_t *)0)->removeTime), sizeof( ((rvmGameDelayRemoveEntry_t *)0)->removeTime ) },
+ { "idEntity *", "entity", (intptr_t)(&((rvmGameDelayRemoveEntry_t *)0)->entity), sizeof( ((rvmGameDelayRemoveEntry_t *)0)->entity ) },
+ { NULL, 0 }
+};
+
static classVariableInfo_t idGameLocal_typeInfo[] = {
{ ": idDict", "serverInfo", (intptr_t)(&((idGameLocal *)0)->serverInfo), sizeof( ((idGameLocal *)0)->serverInfo ) },
{ "int", "numClients", (intptr_t)(&((idGameLocal *)0)->numClients), sizeof( ((idGameLocal *)0)->numClients ) },
@@ -5547,6 +5540,7 @@ static classVariableInfo_t idGameLocal_typeInfo[] = {
{ "idDict", "newInfo", (intptr_t)(&((idGameLocal *)0)->newInfo), sizeof( ((idGameLocal *)0)->newInfo ) },
{ "idStrList", "shakeSounds", (intptr_t)(&((idGameLocal *)0)->shakeSounds), sizeof( ((idGameLocal *)0)->shakeSounds ) },
{ "byte[16384]", "lagometer", (intptr_t)(&((idGameLocal *)0)->lagometer), sizeof( ((idGameLocal *)0)->lagometer ) },
+ { "idList < rvmGameDelayRemoveEntry_t >", "delayRemoveEntities", (intptr_t)(&((idGameLocal *)0)->delayRemoveEntities), sizeof( ((idGameLocal *)0)->delayRemoveEntities ) },
{ NULL, 0 }
};
@@ -6772,20 +6766,39 @@ static classVariableInfo_t idDebris_typeInfo[] = {
{ NULL, 0 }
};
+static classVariableInfo_t WeaponParticle_t_typeInfo[] = {
+ { "char[64]", "name", (intptr_t)(&((WeaponParticle_t *)0)->name), sizeof( ((WeaponParticle_t *)0)->name ) },
+ { "char[128]", "particlename", (intptr_t)(&((WeaponParticle_t *)0)->particlename), sizeof( ((WeaponParticle_t *)0)->particlename ) },
+ { "bool", "active", (intptr_t)(&((WeaponParticle_t *)0)->active), sizeof( ((WeaponParticle_t *)0)->active ) },
+ { "int", "startTime", (intptr_t)(&((WeaponParticle_t *)0)->startTime), sizeof( ((WeaponParticle_t *)0)->startTime ) },
+ { "jointHandle_t", "joint", (intptr_t)(&((WeaponParticle_t *)0)->joint), sizeof( ((WeaponParticle_t *)0)->joint ) },
+ { "bool", "smoke", (intptr_t)(&((WeaponParticle_t *)0)->smoke), sizeof( ((WeaponParticle_t *)0)->smoke ) },
+ { "const idDeclParticle *", "particle", (intptr_t)(&((WeaponParticle_t *)0)->particle), sizeof( ((WeaponParticle_t *)0)->particle ) },
+ { "idFuncEmitter *", "emitter", (intptr_t)(&((WeaponParticle_t *)0)->emitter), sizeof( ((WeaponParticle_t *)0)->emitter ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t WeaponLight_t_typeInfo[] = {
+ { "char[64]", "name", (intptr_t)(&((WeaponLight_t *)0)->name), sizeof( ((WeaponLight_t *)0)->name ) },
+ { "bool", "active", (intptr_t)(&((WeaponLight_t *)0)->active), sizeof( ((WeaponLight_t *)0)->active ) },
+ { "int", "startTime", (intptr_t)(&((WeaponLight_t *)0)->startTime), sizeof( ((WeaponLight_t *)0)->startTime ) },
+ { "jointHandle_t", "joint", (intptr_t)(&((WeaponLight_t *)0)->joint), sizeof( ((WeaponLight_t *)0)->joint ) },
+ { "int", "lightHandle", (intptr_t)(&((WeaponLight_t *)0)->lightHandle), sizeof( ((WeaponLight_t *)0)->lightHandle ) },
+ { "renderLight_t", "light", (intptr_t)(&((WeaponLight_t *)0)->light), sizeof( ((WeaponLight_t *)0)->light ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponObject_typeInfo[] = {
+ { ": idWeapon *", "owner", (intptr_t)(&((rvmWeaponObject *)0)->owner), sizeof( ((rvmWeaponObject *)0)->owner ) },
+ { ": rvStateThread", "stateThread", (intptr_t)(&((rvmWeaponObject *)0)->stateThread), sizeof( ((rvmWeaponObject *)0)->stateThread ) },
+ { "float", "next_attack", (intptr_t)(&((rvmWeaponObject *)0)->next_attack), sizeof( ((rvmWeaponObject *)0)->next_attack ) },
+ { NULL, 0 }
+};
+
static classVariableInfo_t idWeapon_typeInfo[] = {
- { ": idScriptBool", "WEAPON_ATTACK", (intptr_t)(&((idWeapon *)0)->WEAPON_ATTACK), sizeof( ((idWeapon *)0)->WEAPON_ATTACK ) },
- { "idScriptBool", "WEAPON_RELOAD", (intptr_t)(&((idWeapon *)0)->WEAPON_RELOAD), sizeof( ((idWeapon *)0)->WEAPON_RELOAD ) },
- { "idScriptBool", "WEAPON_NETRELOAD", (intptr_t)(&((idWeapon *)0)->WEAPON_NETRELOAD), sizeof( ((idWeapon *)0)->WEAPON_NETRELOAD ) },
- { "idScriptBool", "WEAPON_NETENDRELOAD", (intptr_t)(&((idWeapon *)0)->WEAPON_NETENDRELOAD), sizeof( ((idWeapon *)0)->WEAPON_NETENDRELOAD ) },
- { "idScriptBool", "WEAPON_NETFIRING", (intptr_t)(&((idWeapon *)0)->WEAPON_NETFIRING), sizeof( ((idWeapon *)0)->WEAPON_NETFIRING ) },
- { "idScriptBool", "WEAPON_RAISEWEAPON", (intptr_t)(&((idWeapon *)0)->WEAPON_RAISEWEAPON), sizeof( ((idWeapon *)0)->WEAPON_RAISEWEAPON ) },
- { "idScriptBool", "WEAPON_LOWERWEAPON", (intptr_t)(&((idWeapon *)0)->WEAPON_LOWERWEAPON), sizeof( ((idWeapon *)0)->WEAPON_LOWERWEAPON ) },
- { "weaponStatus_t", "status", (intptr_t)(&((idWeapon *)0)->status), sizeof( ((idWeapon *)0)->status ) },
- { "idThread *", "thread", (intptr_t)(&((idWeapon *)0)->thread), sizeof( ((idWeapon *)0)->thread ) },
- { "idStr", "state", (intptr_t)(&((idWeapon *)0)->state), sizeof( ((idWeapon *)0)->state ) },
- { "idStr", "idealState", (intptr_t)(&((idWeapon *)0)->idealState), sizeof( ((idWeapon *)0)->idealState ) },
- { "int", "animBlendFrames", (intptr_t)(&((idWeapon *)0)->animBlendFrames), sizeof( ((idWeapon *)0)->animBlendFrames ) },
+ { ": int", "animBlendFrames", (intptr_t)(&((idWeapon *)0)->animBlendFrames), sizeof( ((idWeapon *)0)->animBlendFrames ) },
{ "int", "animDoneTime", (intptr_t)(&((idWeapon *)0)->animDoneTime), sizeof( ((idWeapon *)0)->animDoneTime ) },
+ { "bool", "isPlayerFlashlight", (intptr_t)(&((idWeapon *)0)->isPlayerFlashlight), sizeof( ((idWeapon *)0)->isPlayerFlashlight ) },
{ "bool", "isLinked", (intptr_t)(&((idWeapon *)0)->isLinked), sizeof( ((idWeapon *)0)->isLinked ) },
{ "idEntity *", "projectileEnt", (intptr_t)(&((idWeapon *)0)->projectileEnt), sizeof( ((idWeapon *)0)->projectileEnt ) },
{ "idPlayer *", "owner", (intptr_t)(&((idWeapon *)0)->owner), sizeof( ((idWeapon *)0)->owner ) },
@@ -6798,6 +6811,7 @@ static classVariableInfo_t idWeapon_typeInfo[] = {
{ "float", "hideOffset", (intptr_t)(&((idWeapon *)0)->hideOffset), sizeof( ((idWeapon *)0)->hideOffset ) },
{ "bool", "hide", (intptr_t)(&((idWeapon *)0)->hide), sizeof( ((idWeapon *)0)->hide ) },
{ "bool", "disabled", (intptr_t)(&((idWeapon *)0)->disabled), sizeof( ((idWeapon *)0)->disabled ) },
+ { "bool", "isFlashLight", (intptr_t)(&((idWeapon *)0)->isFlashLight), sizeof( ((idWeapon *)0)->isFlashLight ) },
{ "int", "berserk", (intptr_t)(&((idWeapon *)0)->berserk), sizeof( ((idWeapon *)0)->berserk ) },
{ "idVec3", "playerViewOrigin", (intptr_t)(&((idWeapon *)0)->playerViewOrigin), sizeof( ((idWeapon *)0)->playerViewOrigin ) },
{ "idMat3", "playerViewAxis", (intptr_t)(&((idWeapon *)0)->playerViewAxis), sizeof( ((idWeapon *)0)->playerViewAxis ) },
@@ -6814,12 +6828,17 @@ static classVariableInfo_t idWeapon_typeInfo[] = {
{ "idDict", "brassDict", (intptr_t)(&((idWeapon *)0)->brassDict), sizeof( ((idWeapon *)0)->brassDict ) },
{ "int", "brassDelay", (intptr_t)(&((idWeapon *)0)->brassDelay), sizeof( ((idWeapon *)0)->brassDelay ) },
{ "idStr", "icon", (intptr_t)(&((idWeapon *)0)->icon), sizeof( ((idWeapon *)0)->icon ) },
+ { "idStr", "pdaIcon", (intptr_t)(&((idWeapon *)0)->pdaIcon), sizeof( ((idWeapon *)0)->pdaIcon ) },
+ { "idStr", "displayName", (intptr_t)(&((idWeapon *)0)->displayName), sizeof( ((idWeapon *)0)->displayName ) },
+ { "idStr", "itemDesc", (intptr_t)(&((idWeapon *)0)->itemDesc), sizeof( ((idWeapon *)0)->itemDesc ) },
{ "renderLight_t", "guiLight", (intptr_t)(&((idWeapon *)0)->guiLight), sizeof( ((idWeapon *)0)->guiLight ) },
{ "int", "guiLightHandle", (intptr_t)(&((idWeapon *)0)->guiLightHandle), sizeof( ((idWeapon *)0)->guiLightHandle ) },
{ "renderLight_t", "muzzleFlash", (intptr_t)(&((idWeapon *)0)->muzzleFlash), sizeof( ((idWeapon *)0)->muzzleFlash ) },
{ "int", "muzzleFlashHandle", (intptr_t)(&((idWeapon *)0)->muzzleFlashHandle), sizeof( ((idWeapon *)0)->muzzleFlashHandle ) },
{ "renderLight_t", "worldMuzzleFlash", (intptr_t)(&((idWeapon *)0)->worldMuzzleFlash), sizeof( ((idWeapon *)0)->worldMuzzleFlash ) },
{ "int", "worldMuzzleFlashHandle", (intptr_t)(&((idWeapon *)0)->worldMuzzleFlashHandle), sizeof( ((idWeapon *)0)->worldMuzzleFlashHandle ) },
+ { "float", "fraccos", (intptr_t)(&((idWeapon *)0)->fraccos), sizeof( ((idWeapon *)0)->fraccos ) },
+ { "float", "fraccos2", (intptr_t)(&((idWeapon *)0)->fraccos2), sizeof( ((idWeapon *)0)->fraccos2 ) },
{ "idVec3", "flashColor", (intptr_t)(&((idWeapon *)0)->flashColor), sizeof( ((idWeapon *)0)->flashColor ) },
{ "int", "muzzleFlashEnd", (intptr_t)(&((idWeapon *)0)->muzzleFlashEnd), sizeof( ((idWeapon *)0)->muzzleFlashEnd ) },
{ "int", "flashTime", (intptr_t)(&((idWeapon *)0)->flashTime), sizeof( ((idWeapon *)0)->flashTime ) },
@@ -6835,7 +6854,7 @@ static classVariableInfo_t idWeapon_typeInfo[] = {
{ "ammo_t", "ammoType", (intptr_t)(&((idWeapon *)0)->ammoType), sizeof( ((idWeapon *)0)->ammoType ) },
{ "int", "ammoRequired", (intptr_t)(&((idWeapon *)0)->ammoRequired), sizeof( ((idWeapon *)0)->ammoRequired ) },
{ "int", "clipSize", (intptr_t)(&((idWeapon *)0)->clipSize), sizeof( ((idWeapon *)0)->clipSize ) },
- { "int", "ammoClip", (intptr_t)(&((idWeapon *)0)->ammoClip), sizeof( ((idWeapon *)0)->ammoClip ) },
+ { "idPredictedValue < int >", "ammoClip", (intptr_t)(&((idWeapon *)0)->ammoClip), sizeof( ((idWeapon *)0)->ammoClip ) },
{ "int", "lowAmmo", (intptr_t)(&((idWeapon *)0)->lowAmmo), sizeof( ((idWeapon *)0)->lowAmmo ) },
{ "bool", "powerAmmo", (intptr_t)(&((idWeapon *)0)->powerAmmo), sizeof( ((idWeapon *)0)->powerAmmo ) },
{ "bool", "isFiring", (intptr_t)(&((idWeapon *)0)->isFiring), sizeof( ((idWeapon *)0)->isFiring ) },
@@ -6848,6 +6867,9 @@ static classVariableInfo_t idWeapon_typeInfo[] = {
{ "jointHandle_t", "flashJointWorld", (intptr_t)(&((idWeapon *)0)->flashJointWorld), sizeof( ((idWeapon *)0)->flashJointWorld ) },
{ "jointHandle_t", "barrelJointWorld", (intptr_t)(&((idWeapon *)0)->barrelJointWorld), sizeof( ((idWeapon *)0)->barrelJointWorld ) },
{ "jointHandle_t", "ejectJointWorld", (intptr_t)(&((idWeapon *)0)->ejectJointWorld), sizeof( ((idWeapon *)0)->ejectJointWorld ) },
+ { "jointHandle_t", "smokeJointView", (intptr_t)(&((idWeapon *)0)->smokeJointView), sizeof( ((idWeapon *)0)->smokeJointView ) },
+ { "idHashTable < WeaponParticle_t >", "weaponParticles", (intptr_t)(&((idWeapon *)0)->weaponParticles), sizeof( ((idWeapon *)0)->weaponParticles ) },
+ { "idHashTable < WeaponLight_t >", "weaponLights", (intptr_t)(&((idWeapon *)0)->weaponLights), sizeof( ((idWeapon *)0)->weaponLights ) },
{ "const idSoundShader *", "sndHum", (intptr_t)(&((idWeapon *)0)->sndHum), sizeof( ((idWeapon *)0)->sndHum ) },
{ "const idDeclParticle *", "weaponSmoke", (intptr_t)(&((idWeapon *)0)->weaponSmoke), sizeof( ((idWeapon *)0)->weaponSmoke ) },
{ "int", "weaponSmokeStartTime", (intptr_t)(&((idWeapon *)0)->weaponSmokeStartTime), sizeof( ((idWeapon *)0)->weaponSmokeStartTime ) },
@@ -6870,6 +6892,9 @@ static classVariableInfo_t idWeapon_typeInfo[] = {
{ "float", "weaponAngleOffsetMax", (intptr_t)(&((idWeapon *)0)->weaponAngleOffsetMax), sizeof( ((idWeapon *)0)->weaponAngleOffsetMax ) },
{ "float", "weaponOffsetTime", (intptr_t)(&((idWeapon *)0)->weaponOffsetTime), sizeof( ((idWeapon *)0)->weaponOffsetTime ) },
{ "float", "weaponOffsetScale", (intptr_t)(&((idWeapon *)0)->weaponOffsetScale), sizeof( ((idWeapon *)0)->weaponOffsetScale ) },
+ { "int", "grabberState", (intptr_t)(&((idWeapon *)0)->grabberState), sizeof( ((idWeapon *)0)->grabberState ) },
+ { ": rvmWeaponObject *", "currentWeaponObject", (intptr_t)(&((idWeapon *)0)->currentWeaponObject), sizeof( ((idWeapon *)0)->currentWeaponObject ) },
+ { "bool", "OutOfAmmo", (intptr_t)(&((idWeapon *)0)->OutOfAmmo), sizeof( ((idWeapon *)0)->OutOfAmmo ) },
{ NULL, 0 }
};
@@ -8056,6 +8081,125 @@ static classVariableInfo_t idThread_typeInfo[] = {
{ NULL, 0 }
};
+static classVariableInfo_t rvmWeaponFist_typeInfo[] = {
+ { "bool", "side", (intptr_t)(&((rvmWeaponFist *)0)->side), sizeof( ((rvmWeaponFist *)0)->side ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponPistol_typeInfo[] = {
+ { ": float", "spread", (intptr_t)(&((rvmWeaponPistol *)0)->spread), sizeof( ((rvmWeaponPistol *)0)->spread ) },
+ { "const idSoundShader *", "snd_lowammo", (intptr_t)(&((rvmWeaponPistol *)0)->snd_lowammo), sizeof( ((rvmWeaponPistol *)0)->snd_lowammo ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponFlashlight_typeInfo[] = {
+ { "bool", "on", (intptr_t)(&((rvmWeaponFlashlight *)0)->on), sizeof( ((rvmWeaponFlashlight *)0)->on ) },
+ { "float", "intensity", (intptr_t)(&((rvmWeaponFlashlight *)0)->intensity), sizeof( ((rvmWeaponFlashlight *)0)->intensity ) },
+ { "idStr", "skin_on", (intptr_t)(&((rvmWeaponFlashlight *)0)->skin_on), sizeof( ((rvmWeaponFlashlight *)0)->skin_on ) },
+ { "idStr", "skin_on_invis", (intptr_t)(&((rvmWeaponFlashlight *)0)->skin_on_invis), sizeof( ((rvmWeaponFlashlight *)0)->skin_on_invis ) },
+ { "idStr", "skin_off", (intptr_t)(&((rvmWeaponFlashlight *)0)->skin_off), sizeof( ((rvmWeaponFlashlight *)0)->skin_off ) },
+ { "idStr", "skin_off_invis", (intptr_t)(&((rvmWeaponFlashlight *)0)->skin_off_invis), sizeof( ((rvmWeaponFlashlight *)0)->skin_off_invis ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponPDA_typeInfo[] = {
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponShotgun_typeInfo[] = {
+ { ": float", "spread", (intptr_t)(&((rvmWeaponShotgun *)0)->spread), sizeof( ((rvmWeaponShotgun *)0)->spread ) },
+ { "const idSoundShader *", "snd_lowammo", (intptr_t)(&((rvmWeaponShotgun *)0)->snd_lowammo), sizeof( ((rvmWeaponShotgun *)0)->snd_lowammo ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponDoubleShotgun_typeInfo[] = {
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponMachineGun_typeInfo[] = {
+ { ": float", "spread", (intptr_t)(&((rvmWeaponMachineGun *)0)->spread), sizeof( ((rvmWeaponMachineGun *)0)->spread ) },
+ { "const idSoundShader *", "snd_lowammo", (intptr_t)(&((rvmWeaponMachineGun *)0)->snd_lowammo), sizeof( ((rvmWeaponMachineGun *)0)->snd_lowammo ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponPlasmaGun_typeInfo[] = {
+ { ": float", "spread", (intptr_t)(&((rvmWeaponPlasmaGun *)0)->spread), sizeof( ((rvmWeaponPlasmaGun *)0)->spread ) },
+ { "const idSoundShader *", "snd_lowammo", (intptr_t)(&((rvmWeaponPlasmaGun *)0)->snd_lowammo), sizeof( ((rvmWeaponPlasmaGun *)0)->snd_lowammo ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponChainGun_typeInfo[] = {
+ { ": idAnimatedEntity *", "world_model", (intptr_t)(&((rvmWeaponChainGun *)0)->world_model), sizeof( ((rvmWeaponChainGun *)0)->world_model ) },
+ { "jointHandle_t", "world_barrel_joint", (intptr_t)(&((rvmWeaponChainGun *)0)->world_barrel_joint), sizeof( ((rvmWeaponChainGun *)0)->world_barrel_joint ) },
+ { "jointHandle_t", "barrel_joint", (intptr_t)(&((rvmWeaponChainGun *)0)->barrel_joint), sizeof( ((rvmWeaponChainGun *)0)->barrel_joint ) },
+ { "float", "barrel_angle", (intptr_t)(&((rvmWeaponChainGun *)0)->barrel_angle), sizeof( ((rvmWeaponChainGun *)0)->barrel_angle ) },
+ { "float", "current_rate", (intptr_t)(&((rvmWeaponChainGun *)0)->current_rate), sizeof( ((rvmWeaponChainGun *)0)->current_rate ) },
+ { "float", "start_rate", (intptr_t)(&((rvmWeaponChainGun *)0)->start_rate), sizeof( ((rvmWeaponChainGun *)0)->start_rate ) },
+ { "float", "end_rate", (intptr_t)(&((rvmWeaponChainGun *)0)->end_rate), sizeof( ((rvmWeaponChainGun *)0)->end_rate ) },
+ { "float", "spin_start", (intptr_t)(&((rvmWeaponChainGun *)0)->spin_start), sizeof( ((rvmWeaponChainGun *)0)->spin_start ) },
+ { "float", "spin_end", (intptr_t)(&((rvmWeaponChainGun *)0)->spin_end), sizeof( ((rvmWeaponChainGun *)0)->spin_end ) },
+ { "float", "spread", (intptr_t)(&((rvmWeaponChainGun *)0)->spread), sizeof( ((rvmWeaponChainGun *)0)->spread ) },
+ { "int", "numSkipFrames", (intptr_t)(&((rvmWeaponChainGun *)0)->numSkipFrames), sizeof( ((rvmWeaponChainGun *)0)->numSkipFrames ) },
+ { "const idSoundShader *", "snd_windup", (intptr_t)(&((rvmWeaponChainGun *)0)->snd_windup), sizeof( ((rvmWeaponChainGun *)0)->snd_windup ) },
+ { "const idSoundShader *", "snd_winddown", (intptr_t)(&((rvmWeaponChainGun *)0)->snd_winddown), sizeof( ((rvmWeaponChainGun *)0)->snd_winddown ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponRocketLauncher_typeInfo[] = {
+ { "float", "spread", (intptr_t)(&((rvmWeaponRocketLauncher *)0)->spread), sizeof( ((rvmWeaponRocketLauncher *)0)->spread ) },
+ { "idStr", "skin_invisible", (intptr_t)(&((rvmWeaponRocketLauncher *)0)->skin_invisible), sizeof( ((rvmWeaponRocketLauncher *)0)->skin_invisible ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponBFG_typeInfo[] = {
+ { "float", "spread", (intptr_t)(&((rvmWeaponBFG *)0)->spread), sizeof( ((rvmWeaponBFG *)0)->spread ) },
+ { "float", "fuse_start", (intptr_t)(&((rvmWeaponBFG *)0)->fuse_start), sizeof( ((rvmWeaponBFG *)0)->fuse_start ) },
+ { "float", "fuse_end", (intptr_t)(&((rvmWeaponBFG *)0)->fuse_end), sizeof( ((rvmWeaponBFG *)0)->fuse_end ) },
+ { "float", "powerLevel", (intptr_t)(&((rvmWeaponBFG *)0)->powerLevel), sizeof( ((rvmWeaponBFG *)0)->powerLevel ) },
+ { "float", "fire_time", (intptr_t)(&((rvmWeaponBFG *)0)->fire_time), sizeof( ((rvmWeaponBFG *)0)->fire_time ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponHandgrenade_typeInfo[] = {
+ { ": float", "spread", (intptr_t)(&((rvmWeaponHandgrenade *)0)->spread), sizeof( ((rvmWeaponHandgrenade *)0)->spread ) },
+ { "float", "fuse_start", (intptr_t)(&((rvmWeaponHandgrenade *)0)->fuse_start), sizeof( ((rvmWeaponHandgrenade *)0)->fuse_start ) },
+ { "idStr", "skin_nade", (intptr_t)(&((rvmWeaponHandgrenade *)0)->skin_nade), sizeof( ((rvmWeaponHandgrenade *)0)->skin_nade ) },
+ { "idStr", "skin_nade_invis", (intptr_t)(&((rvmWeaponHandgrenade *)0)->skin_nade_invis), sizeof( ((rvmWeaponHandgrenade *)0)->skin_nade_invis ) },
+ { "idStr", "skin_nonade", (intptr_t)(&((rvmWeaponHandgrenade *)0)->skin_nonade), sizeof( ((rvmWeaponHandgrenade *)0)->skin_nonade ) },
+ { "idStr", "skin_nonade_invis", (intptr_t)(&((rvmWeaponHandgrenade *)0)->skin_nonade_invis), sizeof( ((rvmWeaponHandgrenade *)0)->skin_nonade_invis ) },
+ { "idProjectile *", "projectile", (intptr_t)(&((rvmWeaponHandgrenade *)0)->projectile), sizeof( ((rvmWeaponHandgrenade *)0)->projectile ) },
+ { "boolean", "show_grenade", (intptr_t)(&((rvmWeaponHandgrenade *)0)->show_grenade), sizeof( ((rvmWeaponHandgrenade *)0)->show_grenade ) },
+ { ": float", "fuse_end", (intptr_t)(&((rvmWeaponHandgrenade *)0)->fuse_end), sizeof( ((rvmWeaponHandgrenade *)0)->fuse_end ) },
+ { "float", "current_time", (intptr_t)(&((rvmWeaponHandgrenade *)0)->current_time), sizeof( ((rvmWeaponHandgrenade *)0)->current_time ) },
+ { "float", "time_held", (intptr_t)(&((rvmWeaponHandgrenade *)0)->time_held), sizeof( ((rvmWeaponHandgrenade *)0)->time_held ) },
+ { "float", "power", (intptr_t)(&((rvmWeaponHandgrenade *)0)->power), sizeof( ((rvmWeaponHandgrenade *)0)->power ) },
+ { "boolean", "exploded", (intptr_t)(&((rvmWeaponHandgrenade *)0)->exploded), sizeof( ((rvmWeaponHandgrenade *)0)->exploded ) },
+ { "const idSoundShader *", "snd_lowammo", (intptr_t)(&((rvmWeaponHandgrenade *)0)->snd_lowammo), sizeof( ((rvmWeaponHandgrenade *)0)->snd_lowammo ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponChainsaw_typeInfo[] = {
+ { ": bool", "side", (intptr_t)(&((rvmWeaponChainsaw *)0)->side), sizeof( ((rvmWeaponChainsaw *)0)->side ) },
+ { NULL, 0 }
+};
+
+static classVariableInfo_t rvmWeaponGrabber_typeInfo[] = {
+ { "const idSoundShader *", "snd_fireloop", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_fireloop), sizeof( ((rvmWeaponGrabber *)0)->snd_fireloop ) },
+ { "const idSoundShader *", "snd_electroloop", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_electroloop), sizeof( ((rvmWeaponGrabber *)0)->snd_electroloop ) },
+ { "const idSoundShader *", "snd_mainfire", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_mainfire), sizeof( ((rvmWeaponGrabber *)0)->snd_mainfire ) },
+ { "const idSoundShader *", "snd_cangrab", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_cangrab), sizeof( ((rvmWeaponGrabber *)0)->snd_cangrab ) },
+ { "const idSoundShader *", "snd_warning", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_warning), sizeof( ((rvmWeaponGrabber *)0)->snd_warning ) },
+ { "const idSoundShader *", "snd_stopfire", (intptr_t)(&((rvmWeaponGrabber *)0)->snd_stopfire), sizeof( ((rvmWeaponGrabber *)0)->snd_stopfire ) },
+ { ": float", "next_attack", (intptr_t)(&((rvmWeaponGrabber *)0)->next_attack), sizeof( ((rvmWeaponGrabber *)0)->next_attack ) },
+ { "float", "fireStartTime", (intptr_t)(&((rvmWeaponGrabber *)0)->fireStartTime), sizeof( ((rvmWeaponGrabber *)0)->fireStartTime ) },
+ { "bool", "warningBeep1", (intptr_t)(&((rvmWeaponGrabber *)0)->warningBeep1), sizeof( ((rvmWeaponGrabber *)0)->warningBeep1 ) },
+ { "bool", "warningBeep2", (intptr_t)(&((rvmWeaponGrabber *)0)->warningBeep2), sizeof( ((rvmWeaponGrabber *)0)->warningBeep2 ) },
+ { "bool", "warningBeep3", (intptr_t)(&((rvmWeaponGrabber *)0)->warningBeep3), sizeof( ((rvmWeaponGrabber *)0)->warningBeep3 ) },
+ { "bool", "warningBeep4", (intptr_t)(&((rvmWeaponGrabber *)0)->warningBeep4), sizeof( ((rvmWeaponGrabber *)0)->warningBeep4 ) },
+ { "float", "grabberState", (intptr_t)(&((rvmWeaponGrabber *)0)->grabberState), sizeof( ((rvmWeaponGrabber *)0)->grabberState ) },
+ { NULL, 0 }
+};
+
static classTypeInfo_t classTypeInfo[] = {
// { "BITT< unsigned int B >", "", sizeof(BITT< unsigned int B >), BITT_unsigned_int_B__typeInfo },
{ "sysEvent_t", "", sizeof(sysEvent_t), sysEvent_t_typeInfo },
@@ -8081,6 +8225,7 @@ static classTypeInfo_t classTypeInfo[] = {
{ "idSIMDProcessor", "", sizeof(idSIMDProcessor), idSIMDProcessor_typeInfo },
{ "idMath::_flint", "", sizeof(idMath::_flint), idMath__flint_typeInfo },
{ "idMath", "", sizeof(idMath), idMath_typeInfo },
+ { "rvRandom", "", sizeof(rvRandom), rvRandom_typeInfo },
{ "idRandom", "", sizeof(idRandom), idRandom_typeInfo },
{ "idRandom2", "", sizeof(idRandom2), idRandom2_typeInfo },
{ "idComplex", "", sizeof(idComplex), idComplex_typeInfo },
@@ -8228,7 +8373,7 @@ static classTypeInfo_t classTypeInfo[] = {
{ "glRaytracingMeshDesc_t", "", sizeof(glRaytracingMeshDesc_t), glRaytracingMeshDesc_t_typeInfo },
{ "glRaytracingInstanceDesc_t", "", sizeof(glRaytracingInstanceDesc_t), glRaytracingInstanceDesc_t_typeInfo },
{ "glRaytracingVec3_t", "", sizeof(glRaytracingVec3_t), glRaytracingVec3_t_typeInfo },
-// { "glRaytracingLight_s::class_171", "", sizeof(glRaytracingLight_s::class_171), glRaytracingLight_s_class_171_typeInfo },
+// { "glRaytracingLight_s::class_172", "", sizeof(glRaytracingLight_s::class_172), glRaytracingLight_s_class_172_typeInfo },
{ "glRaytracingLight_t", "", sizeof(glRaytracingLight_t), glRaytracingLight_t_typeInfo },
{ "glRaytracingLightingPassDesc_t", "", sizeof(glRaytracingLightingPassDesc_t), glRaytracingLightingPassDesc_t_typeInfo },
{ "QD3D12NeuralPOMImageRGBA8", "", sizeof(QD3D12NeuralPOMImageRGBA8), QD3D12NeuralPOMImageRGBA8_typeInfo },
@@ -8337,7 +8482,7 @@ static classTypeInfo_t classTypeInfo[] = {
{ "jointAnimInfo_t", "", sizeof(jointAnimInfo_t), jointAnimInfo_t_typeInfo },
{ "jointMod_t", "", sizeof(jointMod_t), jointMod_t_typeInfo },
{ "frameLookup_t", "", sizeof(frameLookup_t), frameLookup_t_typeInfo },
-// { "class_280::class_280", "", sizeof(class_280::class_280), class_280_class_280_typeInfo },
+// { "class_281::class_281", "", sizeof(class_281::class_281), class_281_class_281_typeInfo },
{ "frameCommand_t", "", sizeof(frameCommand_t), frameCommand_t_typeInfo },
{ "animFlags_t", "", sizeof(animFlags_t), animFlags_t_typeInfo },
{ "idModelExport", "", sizeof(idModelExport), idModelExport_typeInfo },
@@ -8370,6 +8515,7 @@ static classTypeInfo_t classTypeInfo[] = {
{ "spawnSpot_t", "", sizeof(spawnSpot_t), spawnSpot_t_typeInfo },
{ "idEventQueue", "", sizeof(idEventQueue), idEventQueue_typeInfo },
// { "idEntityPtr< class type >", "", sizeof(idEntityPtr< class type >), idEntityPtr_class_type__typeInfo },
+ { "rvmGameDelayRemoveEntry_t", "", sizeof(rvmGameDelayRemoveEntry_t), rvmGameDelayRemoveEntry_t_typeInfo },
{ "idGameLocal", "idGame", sizeof(idGameLocal), idGameLocal_typeInfo },
{ "idGameError", "idException", sizeof(idGameError), idGameError_typeInfo },
{ "idForce", "idClass", sizeof(idForce), idForce_typeInfo },
@@ -8490,6 +8636,10 @@ static classTypeInfo_t classTypeInfo[] = {
{ "beamTarget_t", "", sizeof(beamTarget_t), beamTarget_t_typeInfo },
{ "idBFGProjectile", "idProjectile", sizeof(idBFGProjectile), idBFGProjectile_typeInfo },
{ "idDebris", "idEntity", sizeof(idDebris), idDebris_typeInfo },
+// { "idPredictedValue< class type_ >", "", sizeof(idPredictedValue< class type_ >), idPredictedValue_class_type___typeInfo },
+ { "WeaponParticle_t", "", sizeof(WeaponParticle_t), WeaponParticle_t_typeInfo },
+ { "WeaponLight_t", "", sizeof(WeaponLight_t), WeaponLight_t_typeInfo },
+ { "rvmWeaponObject", "idClass", sizeof(rvmWeaponObject), rvmWeaponObject_typeInfo },
{ "idWeapon", "idAnimatedEntity", sizeof(idWeapon), idWeapon_typeInfo },
{ "idLight", "idEntity", sizeof(idLight), idLight_typeInfo },
{ "idWorldspawn", "idEntity", sizeof(idWorldspawn), idWorldspawn_typeInfo },
@@ -8593,6 +8743,20 @@ static classTypeInfo_t classTypeInfo[] = {
{ "prstack_t", "", sizeof(prstack_t), prstack_t_typeInfo },
{ "idInterpreter", "", sizeof(idInterpreter), idInterpreter_typeInfo },
{ "idThread", "idClass", sizeof(idThread), idThread_typeInfo },
+ { "rvmWeaponFist", "rvmWeaponObject", sizeof(rvmWeaponFist), rvmWeaponFist_typeInfo },
+ { "rvmWeaponPistol", "rvmWeaponObject", sizeof(rvmWeaponPistol), rvmWeaponPistol_typeInfo },
+ { "rvmWeaponFlashlight", "rvmWeaponObject", sizeof(rvmWeaponFlashlight), rvmWeaponFlashlight_typeInfo },
+ { "rvmWeaponPDA", "rvmWeaponObject", sizeof(rvmWeaponPDA), rvmWeaponPDA_typeInfo },
+ { "rvmWeaponShotgun", "rvmWeaponObject", sizeof(rvmWeaponShotgun), rvmWeaponShotgun_typeInfo },
+ { "rvmWeaponDoubleShotgun", "rvmWeaponObject", sizeof(rvmWeaponDoubleShotgun), rvmWeaponDoubleShotgun_typeInfo },
+ { "rvmWeaponMachineGun", "rvmWeaponObject", sizeof(rvmWeaponMachineGun), rvmWeaponMachineGun_typeInfo },
+ { "rvmWeaponPlasmaGun", "rvmWeaponObject", sizeof(rvmWeaponPlasmaGun), rvmWeaponPlasmaGun_typeInfo },
+ { "rvmWeaponChainGun", "rvmWeaponObject", sizeof(rvmWeaponChainGun), rvmWeaponChainGun_typeInfo },
+ { "rvmWeaponRocketLauncher", "rvmWeaponObject", sizeof(rvmWeaponRocketLauncher), rvmWeaponRocketLauncher_typeInfo },
+ { "rvmWeaponBFG", "rvmWeaponObject", sizeof(rvmWeaponBFG), rvmWeaponBFG_typeInfo },
+ { "rvmWeaponHandgrenade", "rvmWeaponObject", sizeof(rvmWeaponHandgrenade), rvmWeaponHandgrenade_typeInfo },
+ { "rvmWeaponChainsaw", "rvmWeaponObject", sizeof(rvmWeaponChainsaw), rvmWeaponChainsaw_typeInfo },
+ { "rvmWeaponGrabber", "rvmWeaponObject", sizeof(rvmWeaponGrabber), rvmWeaponGrabber_typeInfo },
{ NULL, NULL, 0, NULL }
};
diff --git a/neo/doom3/game/gamesys/State.cpp b/neo/doom3/game/gamesys/State.cpp
index 530118dd..623abb9a 100644
--- a/neo/doom3/game/gamesys/State.cpp
+++ b/neo/doom3/game/gamesys/State.cpp
@@ -312,17 +312,17 @@ stateResult_t rvStateThread::Execute( void )
stateName = call->state;
stateStage = call->parms.stage;
- if( g_debugState.GetBool() )
- {
- if( call->parms.stage )
- {
- gameLocal.Printf( "%s: %s (%d)\n", ((idEntity *)owner)->GetClassname(), call->state.c_str(), call->parms.stage );
- }
- else
- {
- gameLocal.Printf( "%s: %s\n", ((idEntity*)owner)->GetClassname(), call->state.c_str() );
- }
- }
+ //if( g_debugState.GetBool() )
+ //{
+ // if( call->parms.stage )
+ // {
+ // gameLocal.Printf( "%s: %s (%d)\n", ((idEntity *)owner)->GetClassname(), call->state.c_str(), call->parms.stage );
+ // }
+ // else
+ // {
+ // gameLocal.Printf( "%s: %s\n", ((idEntity*)owner)->GetClassname(), call->state.c_str() );
+ // }
+ //}
// Actually call the state function
lastResult = ( stateResult_t )owner->Invoke( call->state, &call->parms );
diff --git a/neo/doom3/game/weapons/Weapon_bfg.cpp b/neo/doom3/game/weapons/Weapon_bfg.cpp
new file mode 100644
index 00000000..ef12b3fb
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_bfg.cpp
@@ -0,0 +1,355 @@
+// Weapon_BFG.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponBFG )
+END_CLASS
+
+#define BFG_MINRELEASETIME 0.05
+#define BFG_FUSE 2.0
+#define BFG_SHORTFUSE 0.5
+#define BFG_MAXPOWER 4.0
+#define BFG_FIRERATE 4
+#define BFG_FIREDELAY 4
+#define BFG_NUMPROJECTILES 1
+
+// blend times
+#define BFG_IDLE_TO_LOWER 4
+#define BFG_IDLE_TO_FIRE 4
+#define BFG_IDLE_TO_RELOAD 4
+#define BFG_RAISE_TO_IDLE 4
+#define BFG_FIRE_TO_IDLE 4
+#define BFG_RELOAD_TO_IDLE 4
+
+/*
+===============
+rvmWeaponBFG::Init
+===============
+*/
+void rvmWeaponBFG::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ fuse_start = 0;
+ fuse_end = 0;
+ powerLevel = 0;
+ fire_time = 0;
+ spread = weapon->GetFloat( "spread" );
+ owner->Event_SetGuiFloat( "powerlevel", 0 );
+ owner->Event_SetGuiFloat( "overcharge", 0 );
+}
+
+/*
+===============
+rvmWeaponBFG::Raise
+===============
+*/
+stateResult_t rvmWeaponBFG::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, BFG_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponBFG::Lower
+===============
+*/
+stateResult_t rvmWeaponBFG::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponBFG::Idle
+===============
+*/
+stateResult_t rvmWeaponBFG::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+
+ if( !owner->AmmoInClip() )
+ {
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle_empty" );
+ owner->Event_WeaponOutOfAmmo();
+ }
+ else
+ {
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ owner->Event_WeaponReady();
+ }
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponBFG::Fire
+===============
+*/
+stateResult_t rvmWeaponBFG::Fire( stateParms_t* parms )
+{
+ float time_held;
+ float power = 0.0f;
+ float intensity;
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_FUSING,
+ FIRE_SHORTFUSE,
+ FIRE_SHORTFUSEWAIT,
+ FIRE_FIRE,
+ FIRE_FIRE_ANIMWAIT,
+ FIRE_FIRE_COOLDOWN,
+ FIRE_DONE
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire_begin", false );
+ fuse_start = gameLocal.time;
+ fuse_end = gameLocal.time + SEC2MS( BFG_FUSE );
+ parms->stage = FIRE_FUSING;
+ return SRESULT_WAIT;
+
+ case FIRE_FUSING:
+ if( !owner->IsFiring() )
+ {
+ parms->stage = FIRE_FIRE;
+ }
+ else if( gameLocal.time > gameLocal.time + SEC2MS( BFG_MINRELEASETIME ) && !owner->IsFiring() )
+ {
+ parms->stage = FIRE_SHORTFUSE;
+ }
+ else if( gameLocal.time >= fuse_end )
+ {
+ parms->stage = FIRE_SHORTFUSE;
+ }
+ else
+ {
+ powerLevel = ( gameLocal.time - fuse_start ) / BFG_FUSE;
+ owner->Event_SetColor( powerLevel, powerLevel, powerLevel );
+ owner->Event_SetGuiFloat( "powerlevel", powerLevel );
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_SHORTFUSE:
+ if( owner->IsFiring() )
+ {
+ fuse_end = gameLocal.time + SEC2MS( BFG_SHORTFUSE );
+ parms->stage = FIRE_SHORTFUSEWAIT;
+ }
+ else
+ {
+ parms->stage = FIRE_FIRE;
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_SHORTFUSEWAIT:
+ if( !owner->IsFiring() || gameLocal.time >= fuse_end )
+ {
+ powerLevel = ( gameLocal.time - fuse_start ) / BFG_FUSE;
+ owner->Event_SetGuiFloat( "powerlevel", powerLevel );
+ parms->stage = FIRE_FIRE;
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_FIRE:
+ if( gameLocal.time >= fuse_end )
+ {
+ OverCharge();
+ parms->stage = FIRE_DONE;
+ }
+ else
+ {
+ time_held = gameLocal.time - fuse_start;
+ if( power > BFG_MAXPOWER )
+ {
+ power = BFG_MAXPOWER;
+ }
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ fire_time = gameLocal.time;
+
+ owner->Event_LaunchProjectiles( BFG_NUMPROJECTILES, spread, 0, 1, 1 );
+ parms->stage = FIRE_FIRE_ANIMWAIT;
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_FIRE_ANIMWAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, BFG_FIRE_TO_IDLE ) )
+ {
+ parms->stage = FIRE_FIRE_COOLDOWN;
+ }
+ else
+ {
+ intensity = 1 - ( ( gameLocal.time - fire_time ) / 0.5 );
+ if( intensity < 0 )
+ {
+ intensity = 0;
+ }
+ owner->Event_SetColor( intensity, intensity, intensity );
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_FIRE_COOLDOWN:
+ if( powerLevel <= 0 )
+ {
+ parms->stage = FIRE_DONE;
+ }
+ else
+ {
+ powerLevel -= SEC2MS( 0.05 ); // TODO: DELTA TIME THIS!!
+ owner->Event_SetGuiFloat( "powerlevel", powerLevel );
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_DONE:
+ owner->Event_SetGuiFloat( "powerlevel", 0 );
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponBFG::OverCharge
+===============
+*/
+
+void rvmWeaponBFG::OverCharge()
+{
+ idStr entname;
+ idEntity* explosion;
+ idVec3 forward;
+ idAngles angles;
+ idPlayer* player;
+
+ player = owner->GetOwner();
+
+ owner->Event_AllowDrop( false );
+ owner->Event_UseAmmo( owner->ClipSize() );
+
+ angles = player->viewAngles;
+ forward = angles.ToForward();
+
+ entname = owner->GetKey( "def_overcharge" );
+ //explosion = sys.spawn(entname);
+ {
+ idDict spawnArgs;
+
+ spawnArgs.Set( "classname", entname );
+ gameLocal.SpawnEntityDef( spawnArgs, &explosion );
+ }
+ explosion->Event_SetOrigin( owner->GetOrigin() + forward * 16 );
+ explosion->Event_SetShaderParm( /*SHADERPARM_TIMEOFFSET*/4, -gameLocal.time );
+ gameLocal.DelayRemoveEntity( explosion, 2 );
+
+ owner->Event_StartSound( "snd_explode", SND_CHANNEL_ANY, false );
+ gameLocal.RadiusDamage( owner->GetOrigin(), owner, owner->GetOwner(), nullptr, nullptr, "damage_bfg_overcharge", 1.0 );
+}
+
+/*
+===============
+rvmWeaponBFG::Reload
+===============
+*/
+stateResult_t rvmWeaponBFG::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_bfg.h b/neo/doom3/game/weapons/Weapon_bfg.h
new file mode 100644
index 00000000..39c090c9
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_bfg.h
@@ -0,0 +1,27 @@
+// Weapon_bfg.h
+//
+
+#pragma once
+
+class rvmWeaponBFG : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponBFG );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void OverCharge();
+
+ float spread;
+
+ float fuse_start;
+ float fuse_end;
+ float powerLevel;
+ float fire_time;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_chaingun.cpp b/neo/doom3/game/weapons/Weapon_chaingun.cpp
new file mode 100644
index 00000000..60f63583
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_chaingun.cpp
@@ -0,0 +1,324 @@
+// Weapon_chaingun.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponChainGun )
+END_CLASS
+
+#define CHAINGUN_FIRE_SKIPFRAMES 7 // 6 shots per second
+#define CHAINGUN_LOWAMMO 10
+#define CHAINGUN_NUMPROJECTILES 1
+#define CHAINGUN_BARREL_SPEED ( 60 * ( USERCMD_HZ / CHAINGUN_FIRE_SKIPFRAMES ) )
+#define CHAINGUN_BARREL_ACCEL_TIME 0.4
+#define CHAINGUN_BARREL_DECCEL_TIME 1.0
+#define CHAINGUN_BARREL_ACCEL ( CHAINGUN_BARREL_SPEED / CHAINGUN_BARREL_ACCEL_TIME )
+#define CHAINGUN_BARREL_DECCEL ( CHAINGUN_BARREL_SPEED / CHAINGUN_BARREL_DECCEL_TIME )
+
+// blend times
+#define CHAINGUN_IDLE_TO_LOWER 4
+#define CHAINGUN_IDLE_TO_FIRE 0
+#define CHAINGUN_IDLE_TO_RELOAD 4
+#define CHAINGUN_RAISE_TO_IDLE 0
+#define CHAINGUN_WINDDOWN_TO_IDLE 0
+#define CHAINGUN_RELOAD_TO_IDLE 0
+
+
+/*
+===============
+rvmWeaponChainGun::Init
+===============
+*/
+void rvmWeaponChainGun::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ world_model = ( idAnimatedEntity* )owner->GetWorldModel();
+ world_barrel_joint = owner->GetAnimator()->GetJointHandle( "toob" );
+ barrel_joint = owner->GetAnimator()->GetJointHandle( "spinner" );
+ barrel_angle = 0;
+ current_rate = 0;
+ start_rate = 0;
+ end_rate = 0;
+ spin_start = 0;
+ numSkipFrames = 0;
+ spin_end = 0;
+ spread = weapon->GetFloat( "spread" ); // weapon->GetFloat("spread")
+ snd_windup = FindSound( "snd_windup" );
+ snd_winddown = FindSound( "snd_winddown" );
+}
+
+/*
+===============
+rvmWeaponChainGun::UpdateBarrel
+===============
+*/
+void rvmWeaponChainGun::UpdateBarrel()
+{
+ float currentTime;
+ //float t;
+ idAngles ang;
+
+ currentTime = gameLocal.time;
+ //if (currentTime < spin_end) {
+ // t = (currentTime - spin_start) / (spin_end - spin_start);
+ // current_rate = start_rate + t * (end_rate - start_rate);
+ //}
+ //else {
+ current_rate = end_rate;
+ //}
+
+ if( current_rate )
+ {
+ barrel_angle = barrel_angle + current_rate * GAME_FRAMETIME;
+
+ ang.pitch = 0;
+ ang.yaw = 0;
+ ang.roll = barrel_angle;
+ owner->Event_SetJointAngle( barrel_joint, JOINTMOD_LOCAL, ang );
+
+ ang.yaw = barrel_angle;
+ ang.roll = 0;
+ world_model->Event_SetJointAngle( world_barrel_joint, JOINTMOD_LOCAL, ang );
+ }
+}
+
+/*
+===============
+rvmWeaponChainGun::SpinUp
+===============
+*/
+void rvmWeaponChainGun::SpinUp()
+{
+ start_rate = current_rate;
+ end_rate = CHAINGUN_BARREL_SPEED;
+ spin_start = gameLocal.time;
+ spin_end = spin_start + ( end_rate - current_rate ) / CHAINGUN_BARREL_ACCEL;
+ owner->Event_StartSound( "snd_windup", SND_CHANNEL_BODY3, false );
+}
+
+/*
+===============
+rvmWeaponChainGun::SpinDown
+===============
+*/
+void rvmWeaponChainGun::SpinDown()
+{
+ start_rate = current_rate;
+ end_rate = 0;
+ spin_start = gameLocal.time;
+ spin_end = spin_start + ( current_rate - end_rate ) / CHAINGUN_BARREL_DECCEL;
+ owner->Event_StartSound( "snd_winddown", SND_CHANNEL_BODY3, false );
+}
+
+/*
+===============
+rvmWeaponChainGun::Raise
+===============
+*/
+stateResult_t rvmWeaponChainGun::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, CHAINGUN_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponChainGun::Lower
+===============
+*/
+stateResult_t rvmWeaponChainGun::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponChainGun::Idle
+===============
+*/
+stateResult_t rvmWeaponChainGun::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+
+ if( !owner->AmmoInClip() )
+ {
+ owner->Event_WeaponOutOfAmmo();
+ }
+ else
+ {
+ owner->Event_WeaponReady();
+ }
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponChainGun::Fire
+===============
+*/
+stateResult_t rvmWeaponChainGun::Fire( stateParms_t* parms )
+{
+ float ammoClip;
+// float currentTime;
+// float skip;
+
+ ammoClip = owner->AmmoInClip();
+
+ enum FireState
+ {
+ FIRE_NOTSET = 0,
+ FIRE_SPIN1,
+ FIRE_FIRE,
+ FIRE_SKIPFRAMES
+ };
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ SpinUp();
+ UpdateBarrel();
+ if( current_rate >= end_rate )
+ {
+ if( ammoClip > 0 )
+ {
+ parms->stage = FIRE_SPIN1;
+ }
+ else
+ {
+ parms->stage = 0;
+ return SRESULT_DONE;
+ }
+ }
+ return SRESULT_WAIT;
+
+ case FIRE_SPIN1:
+ owner->Event_StartSound( "snd_spin", SND_CHANNEL_BODY3, false );
+ parms->stage = FIRE_FIRE;
+ return SRESULT_WAIT;
+
+ case FIRE_FIRE:
+ owner->Event_LaunchProjectiles( CHAINGUN_NUMPROJECTILES, spread, 0, 1, 1 );
+ owner->Event_StartSound( "snd_fire", SND_CHANNEL_BODY3, false );
+ if( ammoClip == CHAINGUN_LOWAMMO )
+ {
+ owner->Event_StartSound( "snd_lowammo", SND_CHANNEL_ITEM, false );
+ }
+ numSkipFrames = 0;
+ parms->stage = FIRE_SKIPFRAMES;
+ return SRESULT_WAIT;
+
+ case FIRE_SKIPFRAMES:
+ UpdateBarrel();
+ numSkipFrames++;
+ if( numSkipFrames >= CHAINGUN_FIRE_SKIPFRAMES )
+ {
+ if( owner->IsFiring() )
+ {
+ parms->stage = FIRE_FIRE;
+ numSkipFrames = 0;
+ }
+ else
+ {
+ SpinDown();
+ return SRESULT_DONE;
+ }
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponChainGun::Reload
+===============
+*/
+stateResult_t rvmWeaponChainGun::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_chaingun.h b/neo/doom3/game/weapons/Weapon_chaingun.h
new file mode 100644
index 00000000..b50433a0
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_chaingun.h
@@ -0,0 +1,37 @@
+// Weapon_chaingun.h
+//
+
+#pragma once
+
+class rvmWeaponChainGun : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponChainGun );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void UpdateBarrel();
+ void SpinUp();
+ void SpinDown();
+private:
+ idAnimatedEntity* world_model;
+ jointHandle_t world_barrel_joint;
+ jointHandle_t barrel_joint;
+ float barrel_angle;
+ float current_rate;
+ float start_rate;
+ float end_rate;
+ float spin_start;
+ float spin_end;
+ float spread;
+ int numSkipFrames;
+
+ const idSoundShader* snd_windup;
+ const idSoundShader* snd_winddown;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_chainsaw.cpp b/neo/doom3/game/weapons/Weapon_chainsaw.cpp
new file mode 100644
index 00000000..504c1adc
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_chainsaw.cpp
@@ -0,0 +1,225 @@
+// Weapon_chainsaw.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponChainsaw )
+END_CLASS
+
+#define CHAINSAW_FIRERATE 0.1
+
+// blend times
+#define CHAINSAW_IDLE_TO_LOWER 4
+#define CHAINSAW_IDLE_TO_FIRE 4
+#define CHAINSAW_RAISE_TO_IDLE 4
+#define CHAINSAW_FIRE_TO_IDLE 4
+
+/*
+===============
+rvmWeaponChainsaw::Init
+===============
+*/
+void rvmWeaponChainsaw::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+}
+
+
+/*
+===============
+rvmWeaponChainsaw::Raise
+===============
+*/
+stateResult_t rvmWeaponChainsaw::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, CHAINSAW_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponChainsaw::Lower
+===============
+*/
+stateResult_t rvmWeaponChainsaw::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponChainsaw::Idle
+===============
+*/
+stateResult_t rvmWeaponChainsaw::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponChainsaw::Reload
+===============
+*/
+stateResult_t rvmWeaponChainsaw::Reload( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
+
+/*
+===============
+rvmWeaponChainsaw::Fire
+===============
+*/
+stateResult_t rvmWeaponChainsaw::Fire( stateParms_t* parms )
+{
+ float currentTime;
+
+ if( parms->stage == 0 )
+ {
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "melee_start", false );
+ owner->Event_Melee();
+ owner->Event_StartSound( "snd_startattack", SND_CHANNEL_WEAPON, false );
+ parms->stage = 1;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 1 )
+ {
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 3 ) )
+ {
+ parms->stage = 2;
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 2 )
+ {
+ owner->Event_StartSound( "snd_attack", SND_CHANNEL_WEAPON, false );
+ parms->stage = 3;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 3 )
+ {
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ parms->stage = 4;
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 4 )
+ {
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "melee_loop" );
+ next_attack = gameLocal.SysScriptTime();
+
+ parms->stage = 5;
+
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 5 )
+ {
+ if( owner->IsFiring() )
+ {
+ currentTime = gameLocal.SysScriptTime();
+ if( currentTime >= next_attack )
+ {
+ owner->Event_Melee();
+ next_attack = currentTime + CHAINSAW_FIRERATE;
+ }
+ return SRESULT_WAIT;
+ }
+ else
+ {
+ parms->stage = 6;
+ return SRESULT_WAIT;
+ }
+ }
+
+ if( parms->stage == 6 )
+ {
+ owner->Event_StartSound( "snd_stopattack", SND_CHANNEL_WEAPON, false );
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "melee_end", false );
+ parms->stage = 7;
+ return SRESULT_WAIT;
+ }
+
+ if( !owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_DONE;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_chainsaw.h b/neo/doom3/game/weapons/Weapon_chainsaw.h
new file mode 100644
index 00000000..328d6143
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_chainsaw.h
@@ -0,0 +1,20 @@
+// Weapon_chainsaw.h
+//
+
+#pragma once
+
+class rvmWeaponChainsaw : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponChainsaw );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ bool side;
+};
diff --git a/neo/doom3/game/weapons/Weapon_double_shotgun.cpp b/neo/doom3/game/weapons/Weapon_double_shotgun.cpp
new file mode 100644
index 00000000..2b5038c8
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_double_shotgun.cpp
@@ -0,0 +1,252 @@
+// Weapon_double_shotgun.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponDoubleShotgun )
+END_CLASS
+
+
+//#define SHOTGUN_DOUBLE_FIRERATE 1.333
+#define SHOTGUN_DOUBLE_FIRERATE 2.2
+
+#define SHOTGUN_DOUBLE_REQUIRED 2
+
+// blend times
+#define SHOTGUN_DOUBLE_IDLE_TO_IDLE 0
+#define SHOTGUN_DOUBLE_IDLE_TO_LOWER 4
+#define SHOTGUN_DOUBLE_IDLE_TO_FIRE 0
+#define SHOTGUN_DOUBLE_IDLE_TO_RELOAD 4
+#define SHOTGUN_DOUBLE_IDLE_TO_NOAMMO 4
+#define SHOTGUN_DOUBLE_NOAMMO_TO_RELOAD 4
+#define SHOTGUN_DOUBLE_NOAMMO_TO_IDLE 4
+#define SHOTGUN_DOUBLE_RAISE_TO_IDLE 4
+#define SHOTGUN_DOUBLE_FIRE_TO_IDLE 4
+#define SHOTGUN_DOUBLE_RELOAD_TO_IDLE 4
+#define SHOTGUN_DOUBLE_RELOAD_TO_FIRE 4
+
+
+//Shotgun Projectile Information
+#define SHOTGUN_CENTER_PROJECTILES 8
+//#define SHOTGUN_CENTER_PROJECTILES 7
+#define SHOTGUN_BIG_PROJECTILES 12
+//#define SHOTGUN_BIG_PROJECTILES 13
+
+#define SHOTGUN_CENTER_WIDTH 5
+#define SHOTGUN_CENTER_HEIGHT 10
+//#define SHOTGUN_CENTER_HEIGHT 12
+#define SHOTGUN_BIG_WIDTH 22
+//#define SHOTGUN_BIG_WIDTH 25
+#define SHOTGUN_BIG_HEIGHT 15
+
+/*
+===============
+rvmWeaponDoubleShotgun::Init
+===============
+*/
+void rvmWeaponDoubleShotgun::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+}
+
+/*
+===============
+rvmWeaponDoubleShotgun::Raise
+===============
+*/
+stateResult_t rvmWeaponDoubleShotgun::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, SHOTGUN_DOUBLE_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponDoubleShotgun::Lower
+===============
+*/
+stateResult_t rvmWeaponDoubleShotgun::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponDoubleShotgun::Idle
+===============
+*/
+stateResult_t rvmWeaponDoubleShotgun::Idle( stateParms_t* parms )
+{
+ //float currentTime = 0;
+ float clip_size;
+
+ clip_size = owner->ClipSize();
+
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ if( !owner->AmmoInClip() )
+ {
+ owner->Event_WeaponOutOfAmmo();
+ }
+ else
+ {
+ owner->Event_WeaponReady();
+ }
+
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponDoubleShotgun::Fire
+===============
+*/
+stateResult_t rvmWeaponDoubleShotgun::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ //if (ammoClip == SHOTGUN_LOWAMMO) {
+ // int length;
+ // owner->StartSoundShader(snd_lowammo, SND_CHANNEL_ITEM, 0, false, &length);
+ //}
+
+ //owner->Event_LaunchProjectiles(SHOTGUN_NUMPROJECTILES, spread, 0, 1, 1);
+ owner->Event_LaunchProjectilesEllipse( SHOTGUN_CENTER_PROJECTILES, SHOTGUN_CENTER_WIDTH, SHOTGUN_CENTER_HEIGHT, 0, 1.0 );
+ owner->Event_LaunchProjectilesEllipse( SHOTGUN_BIG_PROJECTILES, SHOTGUN_BIG_WIDTH, SHOTGUN_BIG_HEIGHT, 0, 1.0 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, SHOTGUN_DOUBLE_FIRE_TO_IDLE ) )
+ {
+ owner->Event_WeaponReloading();
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponDoubleShotgun::Reload
+===============
+*/
+stateResult_t rvmWeaponDoubleShotgun::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload_start", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponDoubleShotgun::EjectBrass
+===============
+*/
+void rvmWeaponDoubleShotgun::EjectBrass( void )
+{
+
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_double_shotgun.h b/neo/doom3/game/weapons/Weapon_double_shotgun.h
new file mode 100644
index 00000000..d11e0c69
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_double_shotgun.h
@@ -0,0 +1,22 @@
+// Weapon_double_shotgun.h
+//
+
+#pragma once
+
+class rvmWeaponDoubleShotgun : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponDoubleShotgun );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+
+ void EjectBrass( void );
+private:
+
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_fist.cpp b/neo/doom3/game/weapons/Weapon_fist.cpp
new file mode 100644
index 00000000..f345e497
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_fist.cpp
@@ -0,0 +1,185 @@
+// weapon_fist.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponFist )
+END_CLASS
+
+#define RIFLE_NUMPROJECTILES 1
+
+// blend times
+#define FISTS_IDLE_TO_LOWER 4
+#define FISTS_IDLE_TO_PUNCH 0
+#define FISTS_RAISE_TO_IDLE 4
+#define FISTS_PUNCH_TO_IDLE 1
+
+
+/*
+================
+rvmWeaponFist::Init
+================
+*/
+void rvmWeaponFist::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+}
+
+/*
+================
+rvmWeaponFist::Raise
+================
+*/
+stateResult_t rvmWeaponFist::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, FISTS_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFist::Lower
+================
+*/
+stateResult_t rvmWeaponFist::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFist::Idle
+================
+*/
+stateResult_t rvmWeaponFist::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFist::Fire
+================
+*/
+stateResult_t rvmWeaponFist::Fire( stateParms_t* parms )
+{
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_MELEE,
+ FIRE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, GetFireAnim(), false );
+ parms->stage = FIRE_MELEE;
+ parms->Wait( 0.1f );
+ return SRESULT_WAIT;
+
+ case FIRE_MELEE:
+ owner->Event_Melee();
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ side = !side;
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFist::Reload
+================
+*/
+stateResult_t rvmWeaponFist::Reload( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
+
+/*
+================
+rvmWeaponFist::GetFireAnim
+================
+*/
+const char* rvmWeaponFist::GetFireAnim()
+{
+ if( side )
+ {
+ return "punch_left";
+ }
+ else
+ {
+ return "punch_right";
+ }
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_fist.h b/neo/doom3/game/weapons/Weapon_fist.h
new file mode 100644
index 00000000..8d2c61ef
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_fist.h
@@ -0,0 +1,21 @@
+// Weapon_fist.h
+//
+
+#pragma once
+
+class rvmWeaponFist : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponFist );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ const char* GetFireAnim();
+ bool side;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_flashlight.cpp b/neo/doom3/game/weapons/Weapon_flashlight.cpp
new file mode 100644
index 00000000..584145c6
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_flashlight.cpp
@@ -0,0 +1,254 @@
+// Weapon_flashlight.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponFlashlight )
+END_CLASS
+
+// blend times
+#define FLASHLIGHT_IDLE_TO_LOWER 4
+#define FLASHLIGHT_IDLE_TO_FIRE 2
+#define FLASHLIGHT_IDLE_TO_RELOAD 4
+#define FLASHLIGHT_RAISE_TO_IDLE 4
+#define FLASHLIGHT_FIRE_TO_IDLE 4
+#define FLASHLIGHT_RELOAD_TO_IDLE 4
+
+#define FLASHLIGHT_MIN_SKIN_INTENSITY 0.2
+
+/*
+================
+rvmWeaponFlashlight::Init
+================
+*/
+void rvmWeaponFlashlight::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ skin_on = owner->GetKey( "skin_on" );
+ skin_on_invis = owner->GetKey( "skin_on_invis" );
+ skin_off = owner->GetKey( "skin_off" );
+ skin_off_invis = owner->GetKey( "skin_off_invis" );
+
+ intensity = 1.0;
+
+ owner->Event_SetLightParm( 3, 1.0 );
+ owner->Event_SetShaderParm( 3, 1.0 );
+
+ on = true;
+
+ UpdateSkin();
+}
+
+/*
+================
+rvmWeaponFlashlight::UpdateLightIntensity
+================
+*/
+void rvmWeaponFlashlight::UpdateLightIntensity( void )
+{
+ // TODO this has to interact with scripts somehow
+}
+
+/*
+================
+rvmWeaponFlashlight::UpdateSkin
+================
+*/
+void rvmWeaponFlashlight::UpdateSkin( void )
+{
+ if( on && ( intensity > FLASHLIGHT_MIN_SKIN_INTENSITY ) )
+ {
+ if( !owner->Event_IsInvisible() )
+ {
+ owner->Event_SetSkin( skin_on );
+ }
+ else
+ {
+ owner->Event_SetSkin( skin_on_invis );
+ }
+ }
+ else
+ {
+ if( !owner->Event_IsInvisible() )
+ {
+ owner->Event_SetSkin( skin_off );
+ }
+ else
+ {
+ owner->Event_SetSkin( skin_off_invis );
+ }
+ }
+}
+
+/*
+================
+rvmWeaponFlashlight::Raise
+================
+*/
+stateResult_t rvmWeaponFlashlight::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, FLASHLIGHT_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFlashlight::Lower
+================
+*/
+stateResult_t rvmWeaponFlashlight::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFlashlight::Idle
+================
+*/
+stateResult_t rvmWeaponFlashlight::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponFlashlight::Fire
+================
+*/
+stateResult_t rvmWeaponFlashlight::Fire( stateParms_t* parms )
+{
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_MELEE,
+ FIRE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_MELEE;
+ parms->Wait( 0.1f );
+ return SRESULT_WAIT;
+
+ case FIRE_MELEE:
+ owner->Event_Melee();
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, FLASHLIGHT_FIRE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_DONE;
+}
+
+/*
+================
+rvmWeaponFlashlight::Reload
+================
+*/
+stateResult_t rvmWeaponFlashlight::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_TOGGLEFLASHLIGHT,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_TOGGLEFLASHLIGHT;
+ parms->Wait( 0.2f );
+ return SRESULT_WAIT;
+
+ case RELOAD_TOGGLEFLASHLIGHT:
+ on = !on;
+ UpdateSkin();
+ owner->Event_Flashlight( on );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, FLASHLIGHT_RELOAD_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
diff --git a/neo/doom3/game/weapons/Weapon_flashlight.h b/neo/doom3/game/weapons/Weapon_flashlight.h
new file mode 100644
index 00000000..767cab1e
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_flashlight.h
@@ -0,0 +1,28 @@
+// Weapon_flashlight.h
+//
+
+#pragma once
+
+class rvmWeaponFlashlight : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponFlashlight );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void UpdateSkin( void );
+ void UpdateLightIntensity( void );
+
+ bool on;
+ float intensity;
+ idStr skin_on;
+ idStr skin_on_invis;
+ idStr skin_off;
+ idStr skin_off_invis;
+};
diff --git a/neo/doom3/game/weapons/Weapon_grabber.cpp b/neo/doom3/game/weapons/Weapon_grabber.cpp
new file mode 100644
index 00000000..73332226
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_grabber.cpp
@@ -0,0 +1,400 @@
+// Weapon_grabber.cpp
+//
+
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponGrabber )
+END_CLASS
+
+// blend times
+#define GRABBER_IDLE_TO_LOWER 4
+#define GRABBER_IDLE_TO_OPEN 4
+#define GRABBER_IDLE_TO_OPENFIRE 4
+#define GRABBER_OPEN_TO_CLOSE 4
+#define GRABBER_OPEN_TO_FIRE 4
+#define GRABBER_CLOSE_TO_OPEN 4
+#define GRABBER_CLOSE_TO_IDLE 4
+#define GRABBER_OPENFIRE_TO_FIRE 2
+#define GRABBER_FIRE_TO_CLOSE 4
+#define GRABBER_RAISE_TO_IDLE 4
+
+#define WARNING_BEEP_1 1.5
+#define WARNING_BEEP_2 2.25
+#define WARNING_BEEP_3 2.50
+#define WARNING_BEEP_4 2.75
+
+/*
+===============
+rvmWeaponGrabber::Init
+===============
+*/
+void rvmWeaponGrabber::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+
+ snd_fireloop = FindSound( "snd_fireloop" );
+ snd_electroloop = FindSound( "snd_electroloop" );
+ snd_mainfire = FindSound( "snd_mainfire" );
+ snd_cangrab = FindSound( "snd_cangrab" );
+ snd_warning = FindSound( "snd_warning" );
+ snd_stopfire = FindSound( "snd_stopfire" );
+
+ owner->Event_Grabber( true );
+
+ grabberState = -1;
+}
+
+
+/*
+===============
+rvmWeaponGrabber::Raise
+===============
+*/
+stateResult_t rvmWeaponGrabber::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, GRABBER_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponGrabber::Lower
+===============
+*/
+stateResult_t rvmWeaponGrabber::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+/*
+===============
+rvmWeaponGrabber::CanFire
+
+Original grabber logic had this, so we prevent the fire action from going unless grabState is 1 or 2.
+
+grabState = grabberHasTarget();
+if ( grabState == 1 || grabState == 2 ) {
+ if(WEAPON_ATTACK) {
+ weaponState( "GrabberOpenFire", GRABBER_IDLE_TO_OPENFIRE );
+ }
+}
+===============
+*/
+//bool rvmWeaponGrabber::CanFire()
+//{
+// int grabState = owner->Event_GrabberHasTarget();
+// return grabState == 1 || grabState == 2;
+//}
+
+/*
+===============
+rvmWeaponGrabber::Idle
+===============
+*/
+stateResult_t rvmWeaponGrabber::Idle( stateParms_t* parms )
+{
+ int grabState = 0;
+
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT,
+ IDLE_GRABBER_OPEN,
+ IDLE_GRABBER_OPEN_WAIT,
+ };
+
+ UpdateGuiLight();
+
+ grabState = owner->Event_GrabberHasTarget();
+ if( grabState == 1 || grabState == 2 )
+ {
+ parms->stage = IDLE_GRABBER_OPEN;
+ }
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ if( !owner->AmmoInClip() )
+ {
+ owner->Event_WeaponOutOfAmmo();
+ }
+ else
+ {
+ owner->Event_WeaponReady();
+ }
+
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_WAIT;
+
+ case IDLE_GRABBER_OPEN:
+ owner->StartSoundShader( snd_fireloop, SND_CHANNEL_BODY3, 0, false, NULL );
+ owner->StartSoundShader( snd_electroloop, SND_CHANNEL_BODY2, 0, false, NULL );
+ owner->StartSoundShader( snd_mainfire, SND_CHANNEL_BODY, 0, false, NULL );
+
+ StartWarningSound();
+ StartActive();
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "open", false );
+ parms->stage = IDLE_GRABBER_OPEN_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_GRABBER_OPEN_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ //owner->WeaponState( WP_FIRE, GRABBER_OPEN_TO_CLOSE );
+ owner->BeginAttack();
+ return SRESULT_DONE;
+ }
+ else
+ {
+ grabState = owner->Event_GrabberHasTarget();
+ if( grabState == 3 )
+ {
+ //owner->WeaponState( WP_FIRE, GRABBER_OPEN_TO_CLOSE );
+ owner->BeginAttack();
+ return SRESULT_DONE;
+ }
+ }
+
+ UpdateGuiLight();
+ UpdateWarningSound();
+ return SRESULT_WAIT;;
+ }
+
+ return SRESULT_DONE;
+}
+
+/*
+=====================
+rvmWeaponGrabber::StartWarningSound
+=====================
+*/
+void rvmWeaponGrabber::StartWarningSound()
+{
+ fireStartTime = gameLocal.realClientTime;
+ warningBeep1 = false;
+ warningBeep2 = false;
+ warningBeep3 = false;
+ warningBeep4 = false;
+}
+
+/*
+=====================
+rvmWeaponGrabber::StartActive
+=====================
+*/
+void rvmWeaponGrabber::StartActive()
+{
+ owner->Event_StartWeaponParticle( "barrel_upper" );
+ owner->Event_StartWeaponLight( "light_barrel_upper" );
+ owner->Event_StartWeaponLight( "light_barrel_lower" );
+ owner->Event_StartWeaponLight( "light_side" );
+}
+
+/*
+=====================
+rvmWeaponGrabber::StopActive
+=====================
+*/
+void rvmWeaponGrabber::StopActive()
+{
+ owner->Event_StopWeaponParticle( "barrel_upper" );
+
+ owner->Event_StopWeaponLight( "light_barrel_upper" );
+ owner->Event_StopWeaponLight( "light_barrel_lower" );
+ owner->Event_StopWeaponLight( "light_side" );
+}
+
+/*
+==================== =
+rvmWeaponGrabber::UpdateGuiLight
+==================== =
+*/
+void rvmWeaponGrabber::UpdateGuiLight()
+{
+
+ float newState = owner->Event_GrabberHasTarget();
+ if( newState != grabberState )
+ {
+ grabberState = newState;
+ if( grabberState == 0 )
+ {
+ owner->Event_StartWeaponLight( "gLightBlue" );
+ owner->Event_StopWeaponLight( "gLightYellow" );
+ owner->Event_StopWeaponLight( "gLightRed" );
+ }
+ else if( grabberState == 1 )
+ {
+ //startSound("snd_cangrab", SND_CHANNEL_ITEM, false);
+ owner->StartSoundShader( snd_cangrab, SND_CHANNEL_ITEM, 0, false, NULL );
+ owner->Event_StartWeaponLight( "gLightYellow" );
+ owner->Event_StopWeaponLight( "gLightBlue" );
+ owner->Event_StopWeaponLight( "gLightRed" );
+ }
+ else if( grabberState == 2 )
+ {
+ owner->Event_StartWeaponLight( "gLightRed" );
+ owner->Event_StopWeaponLight( "gLightBlue" );
+ owner->Event_StopWeaponLight( "gLightYellow" );
+ }
+ }
+}
+
+/*
+=====================
+rvmWeaponGrabber::UpdateWarningSound
+=====================
+*/
+void rvmWeaponGrabber::UpdateWarningSound()
+{
+ float currentTime;
+ float elapsed;
+
+ currentTime = gameLocal.realClientTime;
+ elapsed = currentTime - fireStartTime;
+
+ if( elapsed > WARNING_BEEP_1 && !warningBeep1 )
+ {
+ owner->StartSoundShader( snd_warning, SND_CHANNEL_ITEM, 0, false, NULL );
+ warningBeep1 = true;
+ }
+ if( elapsed > WARNING_BEEP_2 && !warningBeep2 )
+ {
+ owner->StartSoundShader( snd_warning, SND_CHANNEL_ITEM, 0, false, NULL );
+ warningBeep2 = true;
+ }
+ if( elapsed > WARNING_BEEP_3 && !warningBeep3 )
+ {
+ owner->StartSoundShader( snd_warning, SND_CHANNEL_ITEM, 0, false, NULL );
+ warningBeep3 = true;
+ }
+ if( elapsed > WARNING_BEEP_4 && !warningBeep4 )
+ {
+ owner->StartSoundShader( snd_warning, SND_CHANNEL_ITEM, 0, false, NULL );
+ warningBeep4 = true;
+ }
+}
+
+
+/*
+===============
+rvmWeaponGrabber::Fire
+===============
+*/
+stateResult_t rvmWeaponGrabber::Fire( stateParms_t* parms )
+{
+ //int grabState;
+ //
+ //grabState = owner->Event_GrabberHasTarget();
+ //
+ //enum FIRE_State
+ //{
+ // FIRE_NOTSET = 0,
+ // FIRE_WAIT_GRABSTATE,
+ // FIRE_ACTUALLY_FIRE,
+ // FIRE_WAIT
+ //};
+ //
+ //switch( parms->stage )
+ //{
+ // case FIRE_NOTSET:
+ // next_attack = MS2SEC( gameLocal.realClientTime );
+ // owner->Event_PlayAnim( ANIMCHANNEL_ALL, "idleopen", true );
+ // parms->stage = FIRE_WAIT_GRABSTATE;
+ // return SRESULT_WAIT;
+ //
+ // case FIRE_WAIT_GRABSTATE:
+ // if( grabState == 3 || grabState == 0 )
+ // {
+ // parms->stage = FIRE_ACTUALLY_FIRE;
+ // }
+ // return SRESULT_WAIT;
+ //
+ // case FIRE_ACTUALLY_FIRE:
+ // StopActive();
+ //
+ // // Stops fire loop sound
+ // owner->StartSoundShader( snd_stopfire, SND_CHANNEL_BODY3, 0, false, NULL );
+ // owner->StopSound( SND_CHANNEL_BODY2, false );
+ //
+ // owner->Event_StartWeaponSmoke();
+ // owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", true );
+ // break;
+ //
+ // case FIRE_WAIT:
+ // if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ // {
+ // owner->WeaponState( WP_IDLE, GRABBER_CLOSE_TO_IDLE );
+ // firingState = 0;
+ // }
+ // else
+ // {
+ // UpdateGuiLight();
+ // }
+ // break;
+ //}
+ return SRESULT_DONE;
+}
+
+/*
+===============
+rvmWeaponGrabber::Reload
+===============
+*/
+stateResult_t rvmWeaponGrabber::Reload( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_grabber.h b/neo/doom3/game/weapons/Weapon_grabber.h
new file mode 100644
index 00000000..eb3eb2b8
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_grabber.h
@@ -0,0 +1,43 @@
+// Weapon_grabber.h
+//
+
+#pragma once
+
+class rvmWeaponGrabber : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponGrabber );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void StartActive();
+ void StopActive();
+
+ void StartWarningSound();
+ void UpdateWarningSound();
+
+ void UpdateGuiLight();
+
+ const idSoundShader* snd_fireloop;
+ const idSoundShader* snd_electroloop;
+ const idSoundShader* snd_mainfire;
+ const idSoundShader* snd_cangrab;
+ const idSoundShader* snd_warning;
+ const idSoundShader* snd_stopfire;
+private:
+ float next_attack;
+
+ float fireStartTime;
+ bool warningBeep1;
+ bool warningBeep2;
+ bool warningBeep3;
+ bool warningBeep4;
+
+ float grabberState;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_handgrenade.cpp b/neo/doom3/game/weapons/Weapon_handgrenade.cpp
new file mode 100644
index 00000000..26a9678a
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_handgrenade.cpp
@@ -0,0 +1,384 @@
+// Weapon_handgrenade.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponHandgrenade )
+END_CLASS
+
+#define HANDGRENADE_MINRELEASETIME 0.05
+#define HANDGRENADE_FUSE 3
+#define HANDGRENADE_QUICKTHROWTIME .2
+#define HANDGRENADE_MINPOWER 1.5
+#define HANDGRENADE_MAXPOWER 6
+#define HANDGRENADE_QUICKTHROWLAUNCH 0.55
+#define HANDGRENADE_NORMALTHROWLAUNCH 0.3
+#define HANDGRENADE_NUMPROJECTILES 1
+
+// blend times
+#define HANDGRENADE_IDLE_TO_LOWER 4
+#define HANDGRENADE_IDLE_TO_FIRE 4
+#define HANDGRENADE_RAISE_TO_IDLE 4
+#define HANDGRENADE_FIRE_TO_IDLE 4
+
+/*
+===============
+rvmWeaponHandgrenade::Init
+===============
+*/
+void rvmWeaponHandgrenade::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ projectile = NULL;
+ spread = owner->GetFloatKey( "spread" );
+ skin_nade = owner->GetKey( "skin_nade" );
+ skin_nade_invis = owner->GetKey( "skin_nade_invis" );
+ skin_nonade = owner->GetKey( "skin_nonade" );
+ skin_nonade_invis = owner->GetKey( "skin_nonade_invis" );
+
+ GrenadeNade();
+}
+
+/*
+===============
+rvmWeaponHandgrenade::GrenadeNade
+===============
+*/
+void rvmWeaponHandgrenade::GrenadeNade( void )
+{
+ show_grenade = true;
+ UpdateSkin();
+}
+
+/*
+===============
+rvmWeaponHandgrenade::GrenadeNoNade
+===============
+*/
+void rvmWeaponHandgrenade::GrenadeNoNade( void )
+{
+ show_grenade = false;
+ UpdateSkin();
+}
+
+/*
+===============
+rvmWeaponHandgrenade::GrenadeNoNade
+===============
+*/
+void rvmWeaponHandgrenade::UpdateSkin()
+{
+ if( !show_grenade )
+ {
+ if( owner->Event_IsInvisible() )
+ {
+ owner->Event_SetSkin( skin_nonade_invis );
+ }
+ else
+ {
+ owner->Event_SetSkin( skin_nonade );
+ }
+ }
+ else
+ {
+ if( owner->Event_IsInvisible() )
+ {
+ owner->Event_SetSkin( skin_nade_invis );
+ }
+ else
+ {
+ owner->Event_SetSkin( skin_nade );
+ }
+ }
+}
+
+
+/*
+===============
+rvmWeaponHandgrenade::Raise
+===============
+*/
+stateResult_t rvmWeaponHandgrenade::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, HANDGRENADE_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponHandgrenade::Lower
+===============
+*/
+stateResult_t rvmWeaponHandgrenade::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponHandgrenade::Idle
+===============
+*/
+stateResult_t rvmWeaponHandgrenade::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+/*
+===============
+rvmWeaponHandgrenade::OwnerDied
+===============
+*/
+void rvmWeaponHandgrenade::OwnerDied( void )
+{
+ float time_held;
+
+ if( projectile )
+ {
+ time_held = gameLocal.SysScriptTime() - fuse_start;
+ projectile->Show();
+ projectile->Unbind();
+
+ // allow grenade to drop
+ owner->Event_LaunchProjectiles( HANDGRENADE_NUMPROJECTILES, spread, time_held, 0, 1.0 );
+
+ projectile = NULL;
+ }
+}
+
+/*
+===============
+rvmWeaponHandgrenade::Fire
+===============
+*/
+stateResult_t rvmWeaponHandgrenade::Fire( stateParms_t* parms )
+{
+ if( parms->stage == 0 )
+ {
+ projectile = ( idProjectile* )owner->CreateProjectile();
+
+ if( projectile )
+ {
+ projectile->Event_StartSound( "snd_throw", SND_CHANNEL_BODY, true );
+ }
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "throw_start", false );
+
+ current_time = gameLocal.SysScriptTime();
+ fuse_start = current_time;
+ fuse_end = current_time + HANDGRENADE_FUSE;
+ parms->stage = 1;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 1 )
+ {
+ if( current_time < fuse_end )
+ {
+ if( ( current_time > fuse_start + HANDGRENADE_MINRELEASETIME ) )
+ {
+ parms->stage = 2;
+ return SRESULT_WAIT;
+ }
+ current_time = gameLocal.SysScriptTime();
+ return SRESULT_WAIT;
+ }
+
+ parms->stage = 2;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 2 )
+ {
+ time_held = current_time - fuse_start;
+ power = time_held + HANDGRENADE_MINPOWER;
+ if( power > HANDGRENADE_MAXPOWER )
+ {
+ power = HANDGRENADE_MAXPOWER;
+ }
+
+ if( time_held < HANDGRENADE_QUICKTHROWTIME )
+ {
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "throw_quick", false );
+ parms->Wait( HANDGRENADE_QUICKTHROWLAUNCH );
+ exploded = false;
+ }
+ else if( time_held < HANDGRENADE_FUSE )
+ {
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "throw", false );
+ parms->Wait( HANDGRENADE_NORMALTHROWLAUNCH );
+ exploded = false;
+ }
+ else
+ {
+ // no anim. grenade just blows up
+ ExplodeInHand();
+ exploded = true;
+ }
+
+ parms->stage = 3;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 3 )
+ {
+ if( !exploded )
+ {
+ GrenadeNoNade();
+ current_time = gameLocal.SysScriptTime();
+ if( projectile )
+ {
+ projectile->Show();
+ projectile->Unbind();
+ owner->Event_LaunchProjectiles( HANDGRENADE_NUMPROJECTILES, spread, current_time - fuse_start, power, 1.0 );
+ projectile = NULL;
+ }
+
+ parms->stage = 4;
+ return SRESULT_WAIT;
+ }
+
+ parms->stage = 5;
+ return SRESULT_WAIT;
+ }
+
+ if( parms->stage == 4 )
+ {
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, HANDGRENADE_FIRE_TO_IDLE ) )
+ {
+ parms->stage = 5;
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_WAIT;
+ }
+
+ if( !owner->AmmoAvailable() )
+ {
+ stateThread.Clear();
+ stateThread.SetState( "Holstered" );
+ owner->GetOwner()->NextWeapon();
+ }
+ else
+ {
+ GrenadeNade();
+ }
+
+ return SRESULT_DONE;
+}
+
+/*
+===============
+rvmWeaponHandgrenade::Reload
+===============
+*/
+stateResult_t rvmWeaponHandgrenade::Reload( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
+
+/*
+===============
+rvmWeaponHandgrenade::ExplodeInHand
+===============
+*/
+void rvmWeaponHandgrenade::ExplodeInHand()
+{
+ idStr entname;
+ idEntity* explosion;
+ idVec3 forward;
+ idAngles angles;
+ idPlayer* owner;
+
+ if( projectile )
+ {
+ projectile->Event_Remove();
+ projectile = NULL;
+ }
+
+ owner = this->owner->GetOwner();
+
+ GrenadeNoNade();
+ this->owner->Event_StartSound( "snd_explode", SND_CHANNEL_ANY, false );
+
+ //if( !common->IsClient() )
+ {
+ this->owner->Event_UseAmmo( HANDGRENADE_NUMPROJECTILES );
+
+ angles = owner->viewAngles;
+ forward = angles.ToForward();
+
+ entname = this->owner->GetKey( "def_explode_inhand" );
+ explosion = gameLocal.Spawn( entname );
+ explosion->SetOrigin( this->owner->GetOrigin() + forward * 16 );
+ explosion->SetShaderParm( SHADERPARM_TIMEOFFSET, -gameLocal.SysScriptTime() );
+ gameLocal.DelayRemoveEntity( explosion, 2 );
+
+ // this should kill us
+ gameLocal.RadiusDamage( this->owner->GetOrigin(), this->owner, owner, NULL, NULL, this->owner->GetKey( "def_damage_inhand" ), 1.0f );
+ }
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_handgrenade.h b/neo/doom3/game/weapons/Weapon_handgrenade.h
new file mode 100644
index 00000000..c68657ed
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_handgrenade.h
@@ -0,0 +1,44 @@
+// Weapon_handgrenade.h
+//
+
+#pragma once
+
+class rvmWeaponHandgrenade : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponHandgrenade );
+
+ virtual void Init( idWeapon* weapon );
+ virtual void OwnerDied( void ) override;
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void GrenadeNade( void );
+ void GrenadeNoNade( void );
+ void UpdateSkin();
+
+ void ExplodeInHand();
+private:
+ float spread;
+ float fuse_start;
+ idStr skin_nade;
+ idStr skin_nade_invis;
+ idStr skin_nonade;
+ idStr skin_nonade_invis;
+ idProjectile* projectile;
+
+ boolean show_grenade;
+private:
+ float fuse_end;
+ float current_time;
+ float time_held;
+ float power;
+ boolean exploded;
+
+
+ const idSoundShader* snd_lowammo;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_machinegun.cpp b/neo/doom3/game/weapons/Weapon_machinegun.cpp
new file mode 100644
index 00000000..fc92d236
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_machinegun.cpp
@@ -0,0 +1,205 @@
+// Weapon_machinegun.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponMachineGun )
+END_CLASS
+
+#define MACHINEGUN_FIRERATE 0.1 // ~10 per second
+#define MACHINEGUN_LOWAMMO 10
+#define MACHINEGUN_NUMPROJECTILES 1
+
+// blend times
+#define MACHINEGUN_IDLE_TO_LOWER 4
+#define MACHINEGUN_IDLE_TO_FIRE 0
+#define MACHINEGUN_IDLE_TO_RELOAD 4
+#define MACHINEGUN_RAISE_TO_IDLE 4
+#define MACHINEGUN_FIRE_TO_IDLE 0
+#define MACHINEGUN_RELOAD_TO_IDLE 4
+
+
+/*
+===============
+rvmWeaponMachineGun::Init
+===============
+*/
+void rvmWeaponMachineGun::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ spread = weapon->GetFloat( "spread" );
+
+ snd_lowammo = FindSound( "snd_lowammo" );
+}
+
+/*
+===============
+rvmWeaponMachineGun::Raise
+===============
+*/
+stateResult_t rvmWeaponMachineGun::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, MACHINEGUN_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponMachineGun::Lower
+===============
+*/
+stateResult_t rvmWeaponMachineGun::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponMachineGun::Idle
+===============
+*/
+stateResult_t rvmWeaponMachineGun::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponMachineGun::Fire
+===============
+*/
+stateResult_t rvmWeaponMachineGun::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ next_attack = gameLocal.realClientTime + MS2SEC( MACHINEGUN_FIRERATE );
+ owner->Event_LaunchProjectiles( MACHINEGUN_NUMPROJECTILES, spread, 0, 1, 1 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, MACHINEGUN_FIRE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponMachineGun::Reload
+===============
+*/
+stateResult_t rvmWeaponMachineGun::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_machinegun.h b/neo/doom3/game/weapons/Weapon_machinegun.h
new file mode 100644
index 00000000..25375c43
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_machinegun.h
@@ -0,0 +1,22 @@
+// Weapon_machinegun.h
+//
+
+#pragma once
+
+class rvmWeaponMachineGun : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponMachineGun );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ float spread;
+
+ const idSoundShader* snd_lowammo;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_pda.cpp b/neo/doom3/game/weapons/Weapon_pda.cpp
new file mode 100644
index 00000000..d9c39fd4
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_pda.cpp
@@ -0,0 +1,133 @@
+// Weapon_pda.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponPDA )
+END_CLASS
+
+/*
+================
+rvmWeaponPDA::Init
+================
+*/
+void rvmWeaponPDA::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+}
+
+/*
+================
+rvmWeaponPDA::Raise
+================
+*/
+stateResult_t rvmWeaponPDA::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponPDA::Lower
+================
+*/
+stateResult_t rvmWeaponPDA::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+================
+rvmWeaponPDA::Idle
+================
+*/
+stateResult_t rvmWeaponPDA::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->GetOwner()->Event_OpenPDA();
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ if( !owner->GetOwner()->objectiveSystemOpen )
+ {
+ //owner->WeaponState( WP_LOWERING, 0 );
+ owner->LowerWeapon();
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+}
+
+/*
+================
+rvmWeaponPDA::Fire
+================
+*/
+stateResult_t rvmWeaponPDA::Fire( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
+
+/*
+================
+rvmWeaponPDA::Reload
+================
+*/
+stateResult_t rvmWeaponPDA::Reload( stateParms_t* parms )
+{
+ return SRESULT_DONE;
+}
diff --git a/neo/doom3/game/weapons/Weapon_pda.h b/neo/doom3/game/weapons/Weapon_pda.h
new file mode 100644
index 00000000..2dd38522
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_pda.h
@@ -0,0 +1,18 @@
+// Weapon_pda.h
+//
+
+#pragma once
+
+class rvmWeaponPDA : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponPDA );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_pistol.cpp b/neo/doom3/game/weapons/Weapon_pistol.cpp
new file mode 100644
index 00000000..96c15f12
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_pistol.cpp
@@ -0,0 +1,217 @@
+// Weapon_pistol.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponPistol )
+END_CLASS
+
+#define PISTOL_FIRERATE 0.4
+#define PISTOL_LOWAMMO 4
+#define PISTOL_NUMPROJECTILES 1
+
+// blend times
+#define PISTOL_IDLE_TO_LOWER 2
+#define PISTOL_IDLE_TO_FIRE 1
+#define PISTOL_IDLE_TO_RELOAD 3
+#define PISTOL_RAISE_TO_IDLE 3
+#define PISTOL_FIRE_TO_IDLE 4
+#define PISTOL_RELOAD_TO_IDLE 40
+
+/*
+===============
+rvmWeaponPistol::Init
+===============
+*/
+void rvmWeaponPistol::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ spread = weapon->GetFloat( "spread" );
+ snd_lowammo = FindSound( "snd_lowammo" );
+}
+
+/*
+===============
+rvmWeaponPistol::Raise
+===============
+*/
+stateResult_t rvmWeaponPistol::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, PISTOL_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponPistol::Lower
+===============
+*/
+stateResult_t rvmWeaponPistol::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponPistol::Idle
+===============
+*/
+stateResult_t rvmWeaponPistol::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ if( !owner->AmmoInClip() )
+ {
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle_empty" );
+ }
+ else
+ {
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ }
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponPistol::Fire
+===============
+*/
+stateResult_t rvmWeaponPistol::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ next_attack = gameLocal.realClientTime + SEC2MS( PISTOL_FIRERATE );
+
+ if( ammoClip == PISTOL_LOWAMMO )
+ {
+ int length;
+ owner->StartSoundShader( snd_lowammo, SND_CHANNEL_ITEM, 0, false, &length );
+ }
+
+ owner->Event_LaunchProjectiles( PISTOL_NUMPROJECTILES, spread, 0, 1, 1 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, PISTOL_FIRE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponPistol::Reload
+===============
+*/
+stateResult_t rvmWeaponPistol::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_pistol.h b/neo/doom3/game/weapons/Weapon_pistol.h
new file mode 100644
index 00000000..c277cd56
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_pistol.h
@@ -0,0 +1,22 @@
+// Weapon_pistol.h
+//
+
+#pragma once
+
+class rvmWeaponPistol : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponPistol );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ float spread;
+
+ const idSoundShader* snd_lowammo;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_plasmagun.cpp b/neo/doom3/game/weapons/Weapon_plasmagun.cpp
new file mode 100644
index 00000000..a9ef92fc
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_plasmagun.cpp
@@ -0,0 +1,205 @@
+// Weapon_plasmagun.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponPlasmaGun )
+END_CLASS
+
+#define PLASMAGUN_FIRERATE 0.125 //changed by Tim
+#define PLASMAGUN_LOWAMMO 10
+#define PLASMAGUN_NUMPROJECTILES 1
+
+// blend times
+#define PLASMAGUN_IDLE_TO_LOWER 4
+#define PLASMAGUN_IDLE_TO_FIRE 1
+#define PLASMAGUN_IDLE_TO_RELOAD 4
+#define PLASMAGUN_RAISE_TO_IDLE 4
+#define PLASMAGUN_FIRE_TO_IDLE 4
+#define PLASMAGUN_RELOAD_TO_IDLE 4
+
+/*
+===============
+rvmWeaponPlasmaGun::Init
+===============
+*/
+void rvmWeaponPlasmaGun::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ spread = weapon->GetFloat( "spread" );
+ snd_lowammo = FindSound( "snd_lowammo" );
+}
+
+/*
+===============
+rvmWeaponPlasmaGun::Raise
+===============
+*/
+stateResult_t rvmWeaponPlasmaGun::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, PLASMAGUN_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponPlasmaGun::Lower
+===============
+*/
+stateResult_t rvmWeaponPlasmaGun::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponPlasmaGun::Idle
+===============
+*/
+stateResult_t rvmWeaponPlasmaGun::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponPlasmaGun::Fire
+===============
+*/
+stateResult_t rvmWeaponPlasmaGun::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ next_attack = gameLocal.time + SEC2MS( PLASMAGUN_FIRERATE );
+ owner->Event_LaunchProjectiles( PLASMAGUN_NUMPROJECTILES, spread, 0, 1, 1 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, PLASMAGUN_FIRE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponPlasmaGun::Reload
+===============
+*/
+stateResult_t rvmWeaponPlasmaGun::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_WeaponReloading();
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, PLASMAGUN_RELOAD_TO_IDLE ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_plasmagun.h b/neo/doom3/game/weapons/Weapon_plasmagun.h
new file mode 100644
index 00000000..00c3ec78
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_plasmagun.h
@@ -0,0 +1,22 @@
+// Weapon_plasmagun.h
+//
+
+#pragma once
+
+class rvmWeaponPlasmaGun : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponPlasmaGun );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ float spread;
+
+ const idSoundShader* snd_lowammo;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_rocketlauncher.cpp b/neo/doom3/game/weapons/Weapon_rocketlauncher.cpp
new file mode 100644
index 00000000..e667b3d8
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_rocketlauncher.cpp
@@ -0,0 +1,235 @@
+// Weapon_rocketlauncher.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponRocketLauncher )
+END_CLASS
+
+#define ROCKETLAUNCHER_LOWAMMO 1
+#define ROCKETLAUNCHER_NUMPROJECTILES 1
+#define ROCKETLAUNCHER_FIREDELAY 1 // was .75 changed again by Tim to 1
+
+// blend times
+#define ROCKETLAUNCHER_IDLE_TO_LOWER 4
+#define ROCKETLAUNCHER_IDLE_TO_FIRE 0
+#define ROCKETLAUNCHER_IDLE_TO_RELOAD 4
+#define ROCKETLAUNCHER_RAISE_TO_IDLE 4
+#define ROCKETLAUNCHER_FIRE_TO_IDLE 0
+#define ROCKETLAUNCHER_RELOAD_TO_IDLE 4
+#define ROCKETLAUNCHER_RELOAD_FRAME 34 // how many frames from the end of "reload" to fill the clip
+
+/*
+===============
+rvmWeaponRocketLauncher::Init
+===============
+*/
+void rvmWeaponRocketLauncher::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ spread = weapon->GetFloat( "spread" );
+ skin_invisible = weapon->GetKey( "skin_invisible" );
+}
+
+/*
+===============
+rvmWeaponRocketLauncher::UpdateSkin
+===============
+*/
+void rvmWeaponRocketLauncher::UpdateSkin()
+{
+ idStr skinname;
+ int ammoClip;
+
+ if( owner->Event_IsInvisible() )
+ {
+ skinname = skin_invisible;
+ }
+ else
+ {
+ ammoClip = owner->AmmoInClip();
+ if( ammoClip > 5 )
+ {
+ // can happen in MP - weapon Raising with slightly out-of-sync ammoClip
+ ammoClip = 5;
+ }
+ if( ammoClip < 0 )
+ {
+ ammoClip = 0;
+ }
+ skinname = va( "skins/models/weapons/%drox.skin", ammoClip );
+ }
+ owner->Event_SetSkin( skinname );
+}
+
+/*
+===============
+rvmWeaponRocketLauncher::Raise
+===============
+*/
+stateResult_t rvmWeaponRocketLauncher::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, ROCKETLAUNCHER_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponRocketLauncher::Lower
+===============
+*/
+stateResult_t rvmWeaponRocketLauncher::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+/*
+===============
+rvmWeaponRocketLauncher::Idle
+===============
+*/
+stateResult_t rvmWeaponRocketLauncher::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponRocketLauncher::Fire
+===============
+*/
+stateResult_t rvmWeaponRocketLauncher::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ next_attack = gameLocal.realClientTime + SEC2MS( ROCKETLAUNCHER_FIREDELAY );
+ owner->Event_LaunchProjectiles( ROCKETLAUNCHER_NUMPROJECTILES, spread, 0, 1, 1 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, ROCKETLAUNCHER_FIRE_TO_IDLE ) )
+ {
+ UpdateSkin();
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponRocketLauncher::Reload
+===============
+*/
+stateResult_t rvmWeaponRocketLauncher::Reload( stateParms_t* parms )
+{
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ owner->Event_AddToClip( owner->ClipSize() );
+ UpdateSkin();
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_rocketlauncher.h b/neo/doom3/game/weapons/Weapon_rocketlauncher.h
new file mode 100644
index 00000000..67768439
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_rocketlauncher.h
@@ -0,0 +1,23 @@
+// Weapon_rocketlauncher.h
+//
+
+#pragma once
+
+class rvmWeaponRocketLauncher : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponRocketLauncher );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ void UpdateSkin();
+
+ float spread;
+ idStr skin_invisible;
+};
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_shotgun.cpp b/neo/doom3/game/weapons/Weapon_shotgun.cpp
new file mode 100644
index 00000000..7626cacd
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_shotgun.cpp
@@ -0,0 +1,245 @@
+// Weapon_shotgun.cpp
+//
+
+#pragma hdrstop
+#include "precompiled.h"
+#include "../Game_local.h"
+
+CLASS_DECLARATION( rvmWeaponObject, rvmWeaponShotgun )
+END_CLASS
+
+#define SHOTGUN_FIRERATE 1.333
+#define SHOTGUN_LOWAMMO 2
+#define SHOTGUN_RELOADRATE 2
+#define SHOTGUN_NUMPROJECTILES 13
+
+// blend times
+#define SHOTGUN_IDLE_TO_IDLE 0
+#define SHOTGUN_IDLE_TO_LOWER 4
+#define SHOTGUN_IDLE_TO_FIRE 1
+#define SHOTGUN_IDLE_TO_RELOAD 4
+#define SHOTGUN_IDLE_TO_NOAMMO 4
+#define SHOTGUN_NOAMMO_TO_RELOAD 4
+#define SHOTGUN_NOAMMO_TO_IDLE 4
+#define SHOTGUN_RAISE_TO_IDLE 1
+#define SHOTGUN_FIRE_TO_IDLE 4
+#define SHOTGUN_RELOAD_TO_IDLE 4
+#define SHOTGUN_RELOAD_TO_FIRE 4
+#define SHOTGUN_RELOAD_TO_LOWER 2
+
+/*
+===============
+rvmWeaponShotgun::Init
+===============
+*/
+void rvmWeaponShotgun::Init( idWeapon* weapon )
+{
+ rvmWeaponObject::Init( weapon );
+
+ next_attack = 0;
+ spread = weapon->GetFloat( "spread" ); // weapon->GetFloat("spread")
+ snd_lowammo = FindSound( "snd_lowammo" );
+}
+
+/*
+===============
+rvmWeaponShotgun::Raise
+===============
+*/
+stateResult_t rvmWeaponShotgun::Raise( stateParms_t* parms )
+{
+ enum RisingState
+ {
+ RISING_NOTSET = 0,
+ RISING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case RISING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "raise", false );
+ parms->stage = RISING_WAIT;
+ return SRESULT_WAIT;
+
+ case RISING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, SHOTGUN_RAISE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+
+/*
+===============
+rvmWeaponShotgun::Lower
+===============
+*/
+stateResult_t rvmWeaponShotgun::Lower( stateParms_t* parms )
+{
+ enum LoweringState
+ {
+ LOWERING_NOTSET = 0,
+ LOWERING_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case LOWERING_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "putaway", false );
+ parms->stage = LOWERING_WAIT;
+ return SRESULT_WAIT;
+
+ case LOWERING_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ SetState( "Holstered" );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponShotgun::Idle
+===============
+*/
+stateResult_t rvmWeaponShotgun::Idle( stateParms_t* parms )
+{
+ enum IdleState
+ {
+ IDLE_NOTSET = 0,
+ IDLE_WAIT
+ };
+
+ switch( parms->stage )
+ {
+ case IDLE_NOTSET:
+ owner->Event_WeaponReady();
+ owner->Event_PlayCycle( ANIMCHANNEL_ALL, "idle" );
+ parms->stage = IDLE_WAIT;
+ return SRESULT_WAIT;
+
+ case IDLE_WAIT:
+ // Do nothing.
+ return SRESULT_DONE;
+ }
+
+ return SRESULT_ERROR;
+}
+/*
+===============
+rvmWeaponShotgun::Fire
+===============
+*/
+stateResult_t rvmWeaponShotgun::Fire( stateParms_t* parms )
+{
+ int ammoClip = owner->AmmoInClip();
+
+ enum FIRE_State
+ {
+ FIRE_NOTSET = 0,
+ FIRE_WAIT
+ };
+
+ if( ammoClip == 0 && owner->AmmoAvailable() && parms->stage == 0 )
+ {
+ //owner->WeaponState( WP_RELOAD, PISTOL_IDLE_TO_RELOAD );
+ owner->Reload();
+ return SRESULT_DONE;
+ }
+
+ switch( parms->stage )
+ {
+ case FIRE_NOTSET:
+ next_attack = gameLocal.realClientTime + SEC2MS( SHOTGUN_FIRERATE );
+
+ if( ammoClip == SHOTGUN_LOWAMMO )
+ {
+ int length;
+ owner->StartSoundShader( snd_lowammo, SND_CHANNEL_ITEM, 0, false, &length );
+ }
+
+ owner->Event_LaunchProjectiles( SHOTGUN_NUMPROJECTILES, spread, 0, 1, 1 );
+
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "fire", false );
+ parms->stage = FIRE_WAIT;
+ return SRESULT_WAIT;
+
+ case FIRE_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, SHOTGUN_FIRE_TO_IDLE ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+ return SRESULT_ERROR;
+}
+
+/*
+===============
+rvmWeaponShotgun::Reload
+===============
+*/
+stateResult_t rvmWeaponShotgun::Reload( stateParms_t* parms )
+{
+ float ammoClip;
+ float ammoAvail;
+ float clip_size;
+
+ clip_size = owner->ClipSize();
+
+ enum RELOAD_State
+ {
+ RELOAD_NOTSET = 0,
+ RELOAD_WAIT,
+ RELOAD_END
+ };
+
+ ammoAvail = owner->AmmoAvailable();
+ ammoClip = owner->AmmoInClip();
+
+ switch( parms->stage )
+ {
+ case RELOAD_NOTSET:
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload_loop", false );
+ parms->stage = RELOAD_WAIT;
+ return SRESULT_WAIT;
+
+ case RELOAD_WAIT:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ if( ( ammoClip < clip_size ) && ( ammoClip < ammoAvail ) )
+ {
+ parms->stage = RELOAD_NOTSET;
+ owner->Event_AddToClip( SHOTGUN_RELOADRATE );
+ return SRESULT_WAIT;
+ }
+ else
+ {
+ parms->stage = RELOAD_END;
+ owner->Event_PlayAnim( ANIMCHANNEL_ALL, "reload_end", false );
+ return SRESULT_WAIT;
+ }
+ owner->Event_AddToClip( owner->ClipSize() );
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ case RELOAD_END:
+ if( owner->Event_AnimDone( ANIMCHANNEL_ALL, 0 ) )
+ {
+ return SRESULT_DONE;
+ }
+ return SRESULT_WAIT;
+ }
+
+
+ return SRESULT_ERROR;
+}
\ No newline at end of file
diff --git a/neo/doom3/game/weapons/Weapon_shotgun.h b/neo/doom3/game/weapons/Weapon_shotgun.h
new file mode 100644
index 00000000..7bbbaafa
--- /dev/null
+++ b/neo/doom3/game/weapons/Weapon_shotgun.h
@@ -0,0 +1,22 @@
+// Weapon_shotgun.h
+//
+
+#pragma once
+
+class rvmWeaponShotgun : public rvmWeaponObject
+{
+public:
+ CLASS_PROTOTYPE( rvmWeaponShotgun );
+
+ virtual void Init( idWeapon* weapon );
+
+ stateResult_t Raise( stateParms_t* parms );
+ stateResult_t Lower( stateParms_t* parms );
+ stateResult_t Idle( stateParms_t* parms );
+ stateResult_t Fire( stateParms_t* parms );
+ stateResult_t Reload( stateParms_t* parms );
+private:
+ float spread;
+
+ const idSoundShader* snd_lowammo;
+};
\ No newline at end of file
diff --git a/neo/engine/idlib/math/Math.cpp b/neo/engine/idlib/math/Math.cpp
index bd6b4eeb..d6b4241a 100644
--- a/neo/engine/idlib/math/Math.cpp
+++ b/neo/engine/idlib/math/Math.cpp
@@ -178,7 +178,7 @@ void idMath::PolarFromArtesian(idVec3& view, idVec3 artesian)
length = sqrtf((artesian[0] * artesian[0]) + (artesian[1] * artesian[1]));
view[2] = idMath::ATan(length, artesian[2]);
}
-
+#endif
// ================================================================================================
// jscott: fast and reliable random routines
// ================================================================================================
@@ -224,6 +224,10 @@ int rvRandom::Init(void)
return(mSeed);
}
+#ifdef QUAKE4
+// RAVEN BEGIN
+// bdube: added block
+
// ================================================================================================
// Barycentric texture coordinate functions
// Get the *SIGNED* area of a triangle required for barycentric
diff --git a/neo/engine/idlib/math/Math.h b/neo/engine/idlib/math/Math.h
index cb6034ac..a31a350f 100644
--- a/neo/engine/idlib/math/Math.h
+++ b/neo/engine/idlib/math/Math.h
@@ -1064,17 +1064,6 @@ ID_INLINE int idMath::FloatHash(const float* array, const int numFloats) {
return hash;
}
-#ifdef QUAKE4
-// RAVEN BEGIN
-// jscott: fast and reliable random routines
-
-// This is the VC libc version of rand() without multiple seeds per thread or 12 levels
-// of subroutine calls.
-// Both calls have been designed to minimise the inherent number of float <--> int
-// conversions and the additional math required to get the desired value.
-// eg the typical tint = (rand() * 255) / 32768
-// becomes tint = rvRandom::irand( 0, 255 )
-
class rvRandom {
private:
static unsigned long mSeed;
@@ -1099,7 +1088,5 @@ public:
static int irand(int min, int max);
};
-// RAVEN END
-#endif
#endif /* !__MATH_MATH_H__ */