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__ */