From b60d42069246e9b84dc1215ee96fa4a465152bfb Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Sun, 1 Sep 2024 23:57:51 -0700 Subject: [PATCH] Push all the latest commits from this week's worklog, changes for RT2 --- .../{ => decls}/def/player.def | 0 .../{ => decls}/def/spawns.def | 0 .../{ => decls}/def/weapons.def | 0 .../{ => decls}/efx/abandoned.efx | 0 .../{ => decls}/efx/alley.efx | 0 .../{ => decls}/efx/arena.efx | 0 .../{ => decls}/efx/auditorium.efx | 0 .../{ => decls}/efx/bathroom.efx | 0 .../{ => decls}/efx/carpetedhallway.efx | 0 .../resources.pk3dir/{ => decls}/efx/cave.efx | 0 .../{ => decls}/efx/chapel.efx | 0 .../resources.pk3dir/{ => decls}/efx/city.efx | 0 .../{ => decls}/efx/citystreets.efx | 0 .../{ => decls}/efx/concerthall.efx | 0 .../{ => decls}/efx/default.efx | 0 .../{ => decls}/efx/dizzy.efx | 0 .../{ => decls}/efx/drugged.efx | 0 .../{ => decls}/efx/dustyroom.efx | 0 .../{ => decls}/efx/forest.efx | 0 .../{ => decls}/efx/hallway.efx | 0 .../{ => decls}/efx/hangar.efx | 0 .../{ => decls}/efx/library.efx | 0 .../{ => decls}/efx/livingroom.efx | 0 .../{ => decls}/efx/mountains.efx | 0 .../{ => decls}/efx/museum.efx | 0 .../{ => decls}/efx/paddedcell.efx | 0 .../{ => decls}/efx/parkinglot.efx | 0 .../{ => decls}/efx/plain.efx | 0 .../{ => decls}/efx/psychotic.efx | 0 .../{ => decls}/efx/quarry.efx | 0 .../resources.pk3dir/{ => decls}/efx/room.efx | 0 .../{ => decls}/efx/sewerpipe.efx | 0 .../{ => decls}/efx/smallwaterroom.efx | 0 .../{ => decls}/efx/stonecorridor.efx | 0 .../{ => decls}/efx/stoneroom.efx | 0 .../{ => decls}/efx/subway.efx | 0 .../{ => decls}/efx/underpass.efx | 0 .../{ => decls}/efx/underwater.efx | 0 .../{ => decls}/sound/footsteps.sndshd | 0 .../{ => decls}/sound/weapons.sndshd | 0 base/resources.pk3dir/gfx/palette.lmp | Bin 0 -> 768 bytes base/resources.pk3dir/gfx/palette.tga | Bin 0 -> 812 bytes base/resources.pk3dir/glsl/caustics.glsl | 38 - base/resources.pk3dir/glsl/caustics_a.glsl | 39 - base/resources.pk3dir/glsl/clutter.glsl | 41 - base/resources.pk3dir/glsl/decal.glsl | 172 ----- .../glsl/decal_reflectcube.glsl | 56 -- base/resources.pk3dir/glsl/default2d.glsl | 29 - .../glsl/defaultadditivesprite.glsl | 27 - base/resources.pk3dir/glsl/defaultfill.glsl | 20 - base/resources.pk3dir/glsl/defaultskin.glsl | 402 ++++------ base/resources.pk3dir/glsl/defaultskybox.glsl | 30 - base/resources.pk3dir/glsl/defaultsprite.glsl | 74 -- base/resources.pk3dir/glsl/defaultwall.glsl | 329 ++++---- base/resources.pk3dir/glsl/defaultwarp.glsl | 146 ---- base/resources.pk3dir/glsl/depthonly.glsl | 29 - base/resources.pk3dir/glsl/fade.glsl | 44 -- base/resources.pk3dir/glsl/fill.glsl | 33 - .../glsl/fullbright_reflect.glsl | 62 -- base/resources.pk3dir/glsl/heat.glsl | 56 -- base/resources.pk3dir/glsl/lensflare.glsl | 33 - base/resources.pk3dir/glsl/lightmapped.glsl | 245 ------ .../glsl/lightmapped_reflect.glsl | 167 ---- .../glsl/lightmapped_specular.glsl | 166 ---- base/resources.pk3dir/glsl/portal.glsl | 36 - .../glsl/pp_colorcorrect.glsl | 31 - base/resources.pk3dir/glsl/reflect.glsl | 53 -- base/resources.pk3dir/glsl/refract.glsl | 45 -- base/resources.pk3dir/glsl/rtlight.glsl | 267 ++++--- base/resources.pk3dir/glsl/skybox.glsl | 53 -- base/resources.pk3dir/glsl/skybox_hdr.glsl | 63 -- .../glsl/skybox_parallax.glsl | 54 -- base/resources.pk3dir/glsl/skybox_valley.glsl | 53 -- base/resources.pk3dir/glsl/sprite.glsl | 33 - base/resources.pk3dir/glsl/sprite_fixed.glsl | 33 - .../resources.pk3dir/glsl/sprite_vscroll.glsl | 34 - base/resources.pk3dir/glsl/terrain.glsl | 167 ---- base/resources.pk3dir/glsl/terrain_alpha.glsl | 169 ----- .../glsl/terrain_alpha_alt.glsl | 173 ----- base/resources.pk3dir/glsl/terrain_mask.glsl | 172 ----- .../resources.pk3dir/glsl/terrain_skybox.glsl | 120 --- .../resources.pk3dir/glsl/terrain_valley.glsl | 155 ---- base/resources.pk3dir/glsl/unlit.glsl | 38 - base/resources.pk3dir/glsl/vertexlit.glsl | 145 ---- .../glsl/vertexlit_specular.glsl | 148 ---- base/resources.pk3dir/glsl/vertexmap.glsl | 36 - base/resources.pk3dir/glsl/water.glsl | 70 -- base/resources.pk3dir/glsl/water_dirty.glsl | 59 -- base/resources.pk3dir/glsl/water_high.glsl | 54 -- base/resources.pk3dir/glsl/water_sky.glsl | 80 -- base/src/client/hud.qc | 9 +- base/src/server/defs.h | 2 - base/src/server/gamerules_multiplayer.qc | 115 +-- base/src/server/progs.src | 2 - base/test_maps.pk3dir/maps/test/pbr.bsp | Bin 0 -> 1682648 bytes base/test_maps.pk3dir/maps/test/pbr.map | 343 +++++++++ src/botlib/NSBot.h | 1 + src/botlib/NSBot.qc | 18 +- src/botlib/combat.qc | 22 +- src/botlib/cvars.h | 1 - src/client/NSRadar.qc | 4 +- src/client/event.qc | 26 + src/client/font.qc | 2 +- src/client/view.qc | 10 +- src/gs-entbase/server/button_target.qc | 4 +- src/gs-entbase/server/env_spark.qc | 4 +- src/gs-entbase/server/func_breakable.qc | 8 +- src/gs-entbase/server/func_door_rotating.qc | 4 +- src/gs-entbase/server/func_guntarget.qc | 4 +- src/gs-entbase/server/func_physbox.qc | 4 +- src/gs-entbase/server/func_pushable.qc | 4 +- src/gs-entbase/server/func_rot_button.qc | 4 +- src/gs-entbase/server/prop_physics.qc | 23 - src/gs-entbase/server/prop_static.qc | 2 +- src/gs-entbase/shared/ambient_generic.qc | 2 +- src/nav/nodes.qc | 2 +- src/server/NSGameRules.h | 7 +- src/server/NSGameRules.qc | 106 ++- src/server/defs.h | 1 + src/server/entry.qc | 46 +- src/server/mapC.h | 131 +++- src/server/mapC_math.h | 3 +- src/server/mapcycle.qc | 2 +- src/server/maptweaks.qc | 9 +- src/server/scripts.h | 31 + src/server/scripts.qc | 111 ++- src/server/skill.qc | 27 +- src/server/vote.qc | 2 +- src/shared/NSClientPlayer.h | 5 + src/shared/NSClientPlayer.qc | 317 ++++---- src/shared/NSEntity.h | 5 - src/shared/NSEntity.qc | 70 +- src/shared/NSIO.h | 5 + src/shared/NSIO.qc | 51 +- src/shared/NSItem.h | 4 +- src/shared/NSItem.qc | 156 ++-- src/shared/NSMonster.qc | 5 +- src/shared/NSNavAI.h | 13 +- src/shared/NSNavAI.qc | 201 +++-- src/shared/NSPhysicsEntity.qc | 4 +- src/shared/NSProjectile.h | 10 +- src/shared/NSProjectile.qc | 66 +- src/shared/NSRenderableEntity.qc | 15 +- src/shared/NSSoundScape.qc | 4 +- src/shared/NSSurfacePropEntity.h | 2 - src/shared/NSSurfacePropEntity.qc | 18 +- src/shared/NSTrigger.qc | 5 +- src/shared/NSWeapon.h | 19 +- src/shared/NSWeapon.qc | 716 ++++++++++-------- src/shared/NSWeapon_NSNavAI.h | 4 - src/shared/ammo.qc | 6 +- src/shared/defs.h | 22 +- src/shared/entities.h | 3 + src/shared/entityDef.h | 5 +- src/shared/entityDef.qc | 108 ++- src/shared/fteextensions.qc | 2 +- src/shared/materials.qc | 4 +- src/shared/surfaceproperties.qc | 2 + src/vgui/ui.qc | 2 +- 159 files changed, 2443 insertions(+), 5081 deletions(-) rename base/resources.pk3dir/{ => decls}/def/player.def (100%) rename base/resources.pk3dir/{ => decls}/def/spawns.def (100%) rename base/resources.pk3dir/{ => decls}/def/weapons.def (100%) rename base/resources.pk3dir/{ => decls}/efx/abandoned.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/alley.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/arena.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/auditorium.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/bathroom.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/carpetedhallway.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/cave.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/chapel.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/city.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/citystreets.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/concerthall.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/default.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/dizzy.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/drugged.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/dustyroom.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/forest.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/hallway.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/hangar.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/library.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/livingroom.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/mountains.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/museum.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/paddedcell.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/parkinglot.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/plain.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/psychotic.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/quarry.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/room.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/sewerpipe.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/smallwaterroom.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/stonecorridor.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/stoneroom.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/subway.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/underpass.efx (100%) rename base/resources.pk3dir/{ => decls}/efx/underwater.efx (100%) rename base/resources.pk3dir/{ => decls}/sound/footsteps.sndshd (100%) rename base/resources.pk3dir/{ => decls}/sound/weapons.sndshd (100%) create mode 100644 base/resources.pk3dir/gfx/palette.lmp create mode 100644 base/resources.pk3dir/gfx/palette.tga delete mode 100644 base/resources.pk3dir/glsl/caustics.glsl delete mode 100644 base/resources.pk3dir/glsl/caustics_a.glsl delete mode 100644 base/resources.pk3dir/glsl/clutter.glsl delete mode 100644 base/resources.pk3dir/glsl/decal.glsl delete mode 100644 base/resources.pk3dir/glsl/decal_reflectcube.glsl delete mode 100644 base/resources.pk3dir/glsl/default2d.glsl delete mode 100644 base/resources.pk3dir/glsl/defaultadditivesprite.glsl delete mode 100644 base/resources.pk3dir/glsl/defaultfill.glsl delete mode 100644 base/resources.pk3dir/glsl/defaultskybox.glsl delete mode 100644 base/resources.pk3dir/glsl/defaultsprite.glsl delete mode 100644 base/resources.pk3dir/glsl/defaultwarp.glsl delete mode 100644 base/resources.pk3dir/glsl/depthonly.glsl delete mode 100644 base/resources.pk3dir/glsl/fade.glsl delete mode 100644 base/resources.pk3dir/glsl/fill.glsl delete mode 100644 base/resources.pk3dir/glsl/fullbright_reflect.glsl delete mode 100644 base/resources.pk3dir/glsl/heat.glsl delete mode 100644 base/resources.pk3dir/glsl/lensflare.glsl delete mode 100644 base/resources.pk3dir/glsl/lightmapped.glsl delete mode 100644 base/resources.pk3dir/glsl/lightmapped_reflect.glsl delete mode 100644 base/resources.pk3dir/glsl/lightmapped_specular.glsl delete mode 100644 base/resources.pk3dir/glsl/portal.glsl delete mode 100644 base/resources.pk3dir/glsl/pp_colorcorrect.glsl delete mode 100644 base/resources.pk3dir/glsl/reflect.glsl delete mode 100644 base/resources.pk3dir/glsl/refract.glsl delete mode 100644 base/resources.pk3dir/glsl/skybox.glsl delete mode 100644 base/resources.pk3dir/glsl/skybox_hdr.glsl delete mode 100644 base/resources.pk3dir/glsl/skybox_parallax.glsl delete mode 100644 base/resources.pk3dir/glsl/skybox_valley.glsl delete mode 100644 base/resources.pk3dir/glsl/sprite.glsl delete mode 100644 base/resources.pk3dir/glsl/sprite_fixed.glsl delete mode 100644 base/resources.pk3dir/glsl/sprite_vscroll.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain_alpha.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain_alpha_alt.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain_mask.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain_skybox.glsl delete mode 100644 base/resources.pk3dir/glsl/terrain_valley.glsl delete mode 100644 base/resources.pk3dir/glsl/unlit.glsl delete mode 100644 base/resources.pk3dir/glsl/vertexlit.glsl delete mode 100644 base/resources.pk3dir/glsl/vertexlit_specular.glsl delete mode 100644 base/resources.pk3dir/glsl/vertexmap.glsl delete mode 100644 base/resources.pk3dir/glsl/water.glsl delete mode 100644 base/resources.pk3dir/glsl/water_dirty.glsl delete mode 100644 base/resources.pk3dir/glsl/water_high.glsl delete mode 100644 base/resources.pk3dir/glsl/water_sky.glsl create mode 100644 base/test_maps.pk3dir/maps/test/pbr.bsp create mode 100644 base/test_maps.pk3dir/maps/test/pbr.map create mode 100644 src/server/scripts.h delete mode 100644 src/shared/NSWeapon_NSNavAI.h diff --git a/base/resources.pk3dir/def/player.def b/base/resources.pk3dir/decls/def/player.def similarity index 100% rename from base/resources.pk3dir/def/player.def rename to base/resources.pk3dir/decls/def/player.def diff --git a/base/resources.pk3dir/def/spawns.def b/base/resources.pk3dir/decls/def/spawns.def similarity index 100% rename from base/resources.pk3dir/def/spawns.def rename to base/resources.pk3dir/decls/def/spawns.def diff --git a/base/resources.pk3dir/def/weapons.def b/base/resources.pk3dir/decls/def/weapons.def similarity index 100% rename from base/resources.pk3dir/def/weapons.def rename to base/resources.pk3dir/decls/def/weapons.def diff --git a/base/resources.pk3dir/efx/abandoned.efx b/base/resources.pk3dir/decls/efx/abandoned.efx similarity index 100% rename from base/resources.pk3dir/efx/abandoned.efx rename to base/resources.pk3dir/decls/efx/abandoned.efx diff --git a/base/resources.pk3dir/efx/alley.efx b/base/resources.pk3dir/decls/efx/alley.efx similarity index 100% rename from base/resources.pk3dir/efx/alley.efx rename to base/resources.pk3dir/decls/efx/alley.efx diff --git a/base/resources.pk3dir/efx/arena.efx b/base/resources.pk3dir/decls/efx/arena.efx similarity index 100% rename from base/resources.pk3dir/efx/arena.efx rename to base/resources.pk3dir/decls/efx/arena.efx diff --git a/base/resources.pk3dir/efx/auditorium.efx b/base/resources.pk3dir/decls/efx/auditorium.efx similarity index 100% rename from base/resources.pk3dir/efx/auditorium.efx rename to base/resources.pk3dir/decls/efx/auditorium.efx diff --git a/base/resources.pk3dir/efx/bathroom.efx b/base/resources.pk3dir/decls/efx/bathroom.efx similarity index 100% rename from base/resources.pk3dir/efx/bathroom.efx rename to base/resources.pk3dir/decls/efx/bathroom.efx diff --git a/base/resources.pk3dir/efx/carpetedhallway.efx b/base/resources.pk3dir/decls/efx/carpetedhallway.efx similarity index 100% rename from base/resources.pk3dir/efx/carpetedhallway.efx rename to base/resources.pk3dir/decls/efx/carpetedhallway.efx diff --git a/base/resources.pk3dir/efx/cave.efx b/base/resources.pk3dir/decls/efx/cave.efx similarity index 100% rename from base/resources.pk3dir/efx/cave.efx rename to base/resources.pk3dir/decls/efx/cave.efx diff --git a/base/resources.pk3dir/efx/chapel.efx b/base/resources.pk3dir/decls/efx/chapel.efx similarity index 100% rename from base/resources.pk3dir/efx/chapel.efx rename to base/resources.pk3dir/decls/efx/chapel.efx diff --git a/base/resources.pk3dir/efx/city.efx b/base/resources.pk3dir/decls/efx/city.efx similarity index 100% rename from base/resources.pk3dir/efx/city.efx rename to base/resources.pk3dir/decls/efx/city.efx diff --git a/base/resources.pk3dir/efx/citystreets.efx b/base/resources.pk3dir/decls/efx/citystreets.efx similarity index 100% rename from base/resources.pk3dir/efx/citystreets.efx rename to base/resources.pk3dir/decls/efx/citystreets.efx diff --git a/base/resources.pk3dir/efx/concerthall.efx b/base/resources.pk3dir/decls/efx/concerthall.efx similarity index 100% rename from base/resources.pk3dir/efx/concerthall.efx rename to base/resources.pk3dir/decls/efx/concerthall.efx diff --git a/base/resources.pk3dir/efx/default.efx b/base/resources.pk3dir/decls/efx/default.efx similarity index 100% rename from base/resources.pk3dir/efx/default.efx rename to base/resources.pk3dir/decls/efx/default.efx diff --git a/base/resources.pk3dir/efx/dizzy.efx b/base/resources.pk3dir/decls/efx/dizzy.efx similarity index 100% rename from base/resources.pk3dir/efx/dizzy.efx rename to base/resources.pk3dir/decls/efx/dizzy.efx diff --git a/base/resources.pk3dir/efx/drugged.efx b/base/resources.pk3dir/decls/efx/drugged.efx similarity index 100% rename from base/resources.pk3dir/efx/drugged.efx rename to base/resources.pk3dir/decls/efx/drugged.efx diff --git a/base/resources.pk3dir/efx/dustyroom.efx b/base/resources.pk3dir/decls/efx/dustyroom.efx similarity index 100% rename from base/resources.pk3dir/efx/dustyroom.efx rename to base/resources.pk3dir/decls/efx/dustyroom.efx diff --git a/base/resources.pk3dir/efx/forest.efx b/base/resources.pk3dir/decls/efx/forest.efx similarity index 100% rename from base/resources.pk3dir/efx/forest.efx rename to base/resources.pk3dir/decls/efx/forest.efx diff --git a/base/resources.pk3dir/efx/hallway.efx b/base/resources.pk3dir/decls/efx/hallway.efx similarity index 100% rename from base/resources.pk3dir/efx/hallway.efx rename to base/resources.pk3dir/decls/efx/hallway.efx diff --git a/base/resources.pk3dir/efx/hangar.efx b/base/resources.pk3dir/decls/efx/hangar.efx similarity index 100% rename from base/resources.pk3dir/efx/hangar.efx rename to base/resources.pk3dir/decls/efx/hangar.efx diff --git a/base/resources.pk3dir/efx/library.efx b/base/resources.pk3dir/decls/efx/library.efx similarity index 100% rename from base/resources.pk3dir/efx/library.efx rename to base/resources.pk3dir/decls/efx/library.efx diff --git a/base/resources.pk3dir/efx/livingroom.efx b/base/resources.pk3dir/decls/efx/livingroom.efx similarity index 100% rename from base/resources.pk3dir/efx/livingroom.efx rename to base/resources.pk3dir/decls/efx/livingroom.efx diff --git a/base/resources.pk3dir/efx/mountains.efx b/base/resources.pk3dir/decls/efx/mountains.efx similarity index 100% rename from base/resources.pk3dir/efx/mountains.efx rename to base/resources.pk3dir/decls/efx/mountains.efx diff --git a/base/resources.pk3dir/efx/museum.efx b/base/resources.pk3dir/decls/efx/museum.efx similarity index 100% rename from base/resources.pk3dir/efx/museum.efx rename to base/resources.pk3dir/decls/efx/museum.efx diff --git a/base/resources.pk3dir/efx/paddedcell.efx b/base/resources.pk3dir/decls/efx/paddedcell.efx similarity index 100% rename from base/resources.pk3dir/efx/paddedcell.efx rename to base/resources.pk3dir/decls/efx/paddedcell.efx diff --git a/base/resources.pk3dir/efx/parkinglot.efx b/base/resources.pk3dir/decls/efx/parkinglot.efx similarity index 100% rename from base/resources.pk3dir/efx/parkinglot.efx rename to base/resources.pk3dir/decls/efx/parkinglot.efx diff --git a/base/resources.pk3dir/efx/plain.efx b/base/resources.pk3dir/decls/efx/plain.efx similarity index 100% rename from base/resources.pk3dir/efx/plain.efx rename to base/resources.pk3dir/decls/efx/plain.efx diff --git a/base/resources.pk3dir/efx/psychotic.efx b/base/resources.pk3dir/decls/efx/psychotic.efx similarity index 100% rename from base/resources.pk3dir/efx/psychotic.efx rename to base/resources.pk3dir/decls/efx/psychotic.efx diff --git a/base/resources.pk3dir/efx/quarry.efx b/base/resources.pk3dir/decls/efx/quarry.efx similarity index 100% rename from base/resources.pk3dir/efx/quarry.efx rename to base/resources.pk3dir/decls/efx/quarry.efx diff --git a/base/resources.pk3dir/efx/room.efx b/base/resources.pk3dir/decls/efx/room.efx similarity index 100% rename from base/resources.pk3dir/efx/room.efx rename to base/resources.pk3dir/decls/efx/room.efx diff --git a/base/resources.pk3dir/efx/sewerpipe.efx b/base/resources.pk3dir/decls/efx/sewerpipe.efx similarity index 100% rename from base/resources.pk3dir/efx/sewerpipe.efx rename to base/resources.pk3dir/decls/efx/sewerpipe.efx diff --git a/base/resources.pk3dir/efx/smallwaterroom.efx b/base/resources.pk3dir/decls/efx/smallwaterroom.efx similarity index 100% rename from base/resources.pk3dir/efx/smallwaterroom.efx rename to base/resources.pk3dir/decls/efx/smallwaterroom.efx diff --git a/base/resources.pk3dir/efx/stonecorridor.efx b/base/resources.pk3dir/decls/efx/stonecorridor.efx similarity index 100% rename from base/resources.pk3dir/efx/stonecorridor.efx rename to base/resources.pk3dir/decls/efx/stonecorridor.efx diff --git a/base/resources.pk3dir/efx/stoneroom.efx b/base/resources.pk3dir/decls/efx/stoneroom.efx similarity index 100% rename from base/resources.pk3dir/efx/stoneroom.efx rename to base/resources.pk3dir/decls/efx/stoneroom.efx diff --git a/base/resources.pk3dir/efx/subway.efx b/base/resources.pk3dir/decls/efx/subway.efx similarity index 100% rename from base/resources.pk3dir/efx/subway.efx rename to base/resources.pk3dir/decls/efx/subway.efx diff --git a/base/resources.pk3dir/efx/underpass.efx b/base/resources.pk3dir/decls/efx/underpass.efx similarity index 100% rename from base/resources.pk3dir/efx/underpass.efx rename to base/resources.pk3dir/decls/efx/underpass.efx diff --git a/base/resources.pk3dir/efx/underwater.efx b/base/resources.pk3dir/decls/efx/underwater.efx similarity index 100% rename from base/resources.pk3dir/efx/underwater.efx rename to base/resources.pk3dir/decls/efx/underwater.efx diff --git a/base/resources.pk3dir/sound/footsteps.sndshd b/base/resources.pk3dir/decls/sound/footsteps.sndshd similarity index 100% rename from base/resources.pk3dir/sound/footsteps.sndshd rename to base/resources.pk3dir/decls/sound/footsteps.sndshd diff --git a/base/resources.pk3dir/sound/weapons.sndshd b/base/resources.pk3dir/decls/sound/weapons.sndshd similarity index 100% rename from base/resources.pk3dir/sound/weapons.sndshd rename to base/resources.pk3dir/decls/sound/weapons.sndshd diff --git a/base/resources.pk3dir/gfx/palette.lmp b/base/resources.pk3dir/gfx/palette.lmp new file mode 100644 index 0000000000000000000000000000000000000000..567396135d4292bd0dad24331f8ff3ecc9344050 GIT binary patch literal 768 zcmV+b1ONO0000mW5Fj8RFfcGdK|xMVPF-GJZfxff0B&pmd3FE}0Ra^O0UZJY zBmx2~0s}Jw13Ll)Ljna#0|in823i9KVgv_i1P5^h2YCbtfdvQ?5&#<+03#s)FDU>w zEC53@08%>uUP1t7MF4h40ESQimQw(pRsgbC0K!=S(pvy34gfh007(@9T@wIn8UcA3 z2aO&FnjQ_VA`-+e4B$%w@o5D6ixB_23IEV9|L9;42>>4x05>EBMk@waFbiif5qUQj zhdmvcM<}RLF1cJd$8AK~cTVGhU+|G{{-SxkezdW2r=@0;o>+#GOn8b!Xna3ea5GG2 zD??c%Hc%WbLlz%34;U*94jl&w69fg-S**!Yn7l@euRwjGId7CWUxzVKeknR#jeQc|9V4Z4EkzhoESvhu2Eoek1 zSUem^G!`~05GEW77ZC(>jd^Q^Yhiz4S$JAfb5clZNQZgt=DIY)~95osg zED{SN4+$9v1`Y=Q+i0A5P~PCNiVF#s1fUKBv=af$901`k0Qy7#|7aHfhcf@5M*p^D|IC2V yc@*ZV3Hs&*|L;GF$^WVA|I_^YX#xk00|~PO4A%q>{{<2M@0b7e#{d8Q{{a5=TrYd69Dwm}&!wlQ;ORN^N_W~KMFa}Agr9=$t_p=OxB_H0+Yag~XsSqQ;o&3*X?zZ+qkOH+&ud3_iyK3p!9m zf2U8+kTbKcI6@EhqPM3${qrYZz-r#G>Sqeo-wN)IN_|16 zImGLFO?;=>v?0}4$tM@7Rp|k4wk?97Uc`O8fD0zN5Qr7$PR zl4&U;JvT8kdU9&u;N;H1n_GG=H+G$=-u#27ao8EylOseHdRqCCT1|chn^h?1(+N&{ zHt^S|_+Y^MYtORtTb)N6ibhu3`+b?QVsmFczj2|)Z&a6S6;6h+P_%(0m6u}IPHg*W zq~%y&?bwd0{jHw80oTr|yiT_zRG1dX;j2v=w@y*OBwA#Yfh3ebv!FDQX%4Byn_yeV z*+Uvf)Uc#Kqx`!?;>n^7zj!+C3EQU{cE8v<{62bg4wx)poS^oBvR z4uF3QYA-_I7Oek6B4H#FHHH~5EDgu9aV(F(ib>2(;+0Zdl;U+1(M%C-6!E2uh|0*_ zK;R&!fn`9e29pue7eH<ol9ovohA& ztLL8!VuAfc)Ay40h($ literal 0 HcmV?d00001 diff --git a/base/resources.pk3dir/glsl/caustics.glsl b/base/resources.pk3dir/glsl/caustics.glsl deleted file mode 100644 index d35cf49b..00000000 --- a/base/resources.pk3dir/glsl/caustics.glsl +++ /dev/null @@ -1,38 +0,0 @@ -//======= Copyright (c) 2015-2022 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Scrolling shader for patches that get blended on top of existing geometry -// with vertex colors defining fading out -//============================================================================== - -!!ver 110 -!!samps diffuse - -#include "sys/defs.h" - -varying vec2 tex1_c; -varying vec2 tex2_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER - void main ( void ) - { - tex1_c = v_texcoord + vec2(e_time * 0.25, e_time * 0.25); - tex2_c = v_texcoord * 0.5 + vec2(e_time * 0.1, e_time * 0.2); - vex_color = v_colour; - - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main ( void ) - { - vec3 diffuse_f = texture2D( s_diffuse, tex1_c ).rgb * vex_color.a; - diffuse_f *= texture2D( s_diffuse, tex2_c ).rgb * vex_color.a; - - gl_FragColor = vec4(diffuse_f, 1.0); - } -#endif - diff --git a/base/resources.pk3dir/glsl/caustics_a.glsl b/base/resources.pk3dir/glsl/caustics_a.glsl deleted file mode 100644 index b599ee48..00000000 --- a/base/resources.pk3dir/glsl/caustics_a.glsl +++ /dev/null @@ -1,39 +0,0 @@ -//======= Copyright (c) 2015-2022 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Alternate version of caustics that's practically inverted for subtract blends -//============================================================================== - -!!ver 110 -!!samps diffuse - -#include "sys/defs.h" - -varying vec2 tex1_c; -varying vec2 tex2_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER - void main ( void ) - { - tex1_c = v_texcoord + vec2(e_time * 0.25, e_time * 0.25); - tex2_c = v_texcoord * 0.5 + vec2(e_time * 0.1, e_time * 0.2); - vex_color = v_colour; - - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main ( void ) - { - vec3 diffuse_f = texture2D( s_diffuse, tex1_c ).rgb; - diffuse_f *= texture2D( s_diffuse, tex2_c ).rgb; - - diffuse_f = mix(diffuse_f, vec3(1.0,1.0,1.0), 1.0 - vex_color.a); - - gl_FragColor = vec4(diffuse_f, 1.0); - } -#endif - diff --git a/base/resources.pk3dir/glsl/clutter.glsl b/base/resources.pk3dir/glsl/clutter.glsl deleted file mode 100644 index 62816fdf..00000000 --- a/base/resources.pk3dir/glsl/clutter.glsl +++ /dev/null @@ -1,41 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Shader used for fading out surfaces after a certain distance. -// It only has a diffuse map. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffuse=0 - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; -varying float eyedist; - -#ifdef VERTEX_SHADER -void main () -{ - tex_c = v_texcoord; - eyedist = abs( length( e_eyepos - v_position.xyz ) ) / 2048.0; - - if (eyedist > 1.0) { - eyedist = 1.0; - } else if (eyedist < 0.0) { - eyedist = 0.0; - } - - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 diffuse_f = texture2D(s_diffuse, tex_c); - gl_FragColor = vec4( diffuse_f.rgb, (1.0 - eyedist) * diffuse_f.a); -} -#endif diff --git a/base/resources.pk3dir/glsl/decal.glsl b/base/resources.pk3dir/glsl/decal.glsl deleted file mode 100644 index 5ef8a04e..00000000 --- a/base/resources.pk3dir/glsl/decal.glsl +++ /dev/null @@ -1,172 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightmapped surface that sticks to walls. -// -// diffusemap = albedo (rgba) -// normalmap = normal (rgb), reflectmask (a) -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!permu LIGHTSTYLED -!!samps diffuse - -!!samps lightmap -!!samps =BUMP normalmap reflectcube -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!cvardf r_fullbright -!!samps =FAKESHADOWS shadowmap - -#include "sys/defs.h" - -// basics -varying vec2 tex_c; -varying vec2 lm0; - -// unfortunately we do support lightstyles -#if defined(LIGHTSTYLED) -varying vec2 lm1, lm2, lm3; -#endif - -// useful for terrain blending -varying vec4 vex_color; - -// dynamic shadows -#ifdef FAKESHADOWS -varying vec4 vtexprojcoord; -#endif - -#ifdef BUMP -varying vec3 eyevector; -varying mat3 invsurface; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #if defined(LIGHTSTYLED) - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main () - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - - #ifdef BUMP - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot(eyeminusvertex, v_svector.xyz); - eyevector.y = dot(eyeminusvertex, v_tvector.xyz); - eyevector.z = dot(eyeminusvertex, v_normal.xyz); - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - - #if defined(LIGHTSTYLED) - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; - } - - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; -#endif - } - - void main (void) - { - vec4 diffuse_f; - float alpha; - - diffuse_f = texture2D(s_diffuse, tex_c); - diffuse_f.rgb *= diffuse_f.a; - - #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - // the lighting stage for the world - #if defined(BUMP) - vec3 cube_c; - vec3 env_f; - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - float refl = texture2D(s_normalmap, tex_c).a; - diffuse_f.rgb *= lightmap_fragment(normal_f); - - cube_c = reflect(normalize(-eyevector), normal_f.rgb); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - env_f = textureCube(s_reflectcube, cube_c).rgb * (e_lmscale.rgb * 0.25); - diffuse_f.rgb = mix(env_f, diffuse_f.rgb, refl); - #else - diffuse_f.rgb *= lightmap_fragment(); - #endif - - // start blend at half-way point - alpha = diffuse_f.a; - - if (alpha > 1.0) - alpha = 1.0; - - gl_FragColor = vec4(fog3(diffuse_f.rgb), alpha); - } -#endif diff --git a/base/resources.pk3dir/glsl/decal_reflectcube.glsl b/base/resources.pk3dir/glsl/decal_reflectcube.glsl deleted file mode 100644 index 8725374c..00000000 --- a/base/resources.pk3dir/glsl/decal_reflectcube.glsl +++ /dev/null @@ -1,56 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// The diffusemap (monochrome) decides the reflectivity of a surface. -// Using a cube environmentmap as a source for reflectivity. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!samps diffuse normalmap reflectcube - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; -varying mat3 invsurface; -varying vec2 wat_c; - -#ifdef VERTEX_SHADER - void main (void) - { - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot(eyeminusvertex, v_svector.xyz); - eyevector.y = dot(eyeminusvertex, v_tvector.xyz); - eyevector.z = dot(eyeminusvertex, v_normal.xyz); - - tex_c = v_texcoord; - wat_c = tex_c + vec2(e_time * 0.01, sin(e_time) * 0.005); - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main (void) - { - vec3 cube_c; - vec4 out_f = vec4(1.0, 1.0, 1.0, 1.0); - vec4 diffuse_f = texture2D(s_diffuse, tex_c); - cube_c = reflect(normalize(-eyevector), texture2D(s_normalmap, wat_c).rgb * 0.35); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - out_f.rgb = textureCube(s_reflectcube, cube_c).rgb; - out_f.rgb *= diffuse_f.r + diffuse_f.b + diffuse_f.g / 3.0; - - // Add fog to the final fragment - gl_FragColor = fog4(out_f); - } -#endif diff --git a/base/resources.pk3dir/glsl/default2d.glsl b/base/resources.pk3dir/glsl/default2d.glsl deleted file mode 100644 index c6a9f7da..00000000 --- a/base/resources.pk3dir/glsl/default2d.glsl +++ /dev/null @@ -1,29 +0,0 @@ -!!ver 100-450 -!!samps 1 - -varying vec2 tc; -varying vec4 vc; - -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; -void main () -{ - tc = v_texcoord; - vc = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 f = vc; -#ifdef PREMUL - f.rgb *= f.a; -#endif - f *= texture2D(s_t0, tc); - - gl_FragColor = f; -} -#endif diff --git a/base/resources.pk3dir/glsl/defaultadditivesprite.glsl b/base/resources.pk3dir/glsl/defaultadditivesprite.glsl deleted file mode 100644 index a809bfb7..00000000 --- a/base/resources.pk3dir/glsl/defaultadditivesprite.glsl +++ /dev/null @@ -1,27 +0,0 @@ -!!permu FOG -!!samps 1 - -#include "sys/fog.h" -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; -varying vec2 tc; -varying vec4 vc; -void main () -{ - tc = v_texcoord; - vc = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -varying vec2 tc; -varying vec4 vc; -uniform vec4 e_colourident; -void main () -{ - vec4 diffuse_f = texture2D(s_t0, tc); - gl_FragColor = fog4additive(diffuse_f * vc * e_colourident); -} -#endif diff --git a/base/resources.pk3dir/glsl/defaultfill.glsl b/base/resources.pk3dir/glsl/defaultfill.glsl deleted file mode 100644 index af3793fc..00000000 --- a/base/resources.pk3dir/glsl/defaultfill.glsl +++ /dev/null @@ -1,20 +0,0 @@ -!!ver 100-450 - -#ifdef VERTEX_SHADER -attribute vec4 v_colour; -varying vec4 vc; - -void main () -{ - vc = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -varying vec4 vc; -void main () -{ - gl_FragColor = vc; -} -#endif diff --git a/base/resources.pk3dir/glsl/defaultskin.glsl b/base/resources.pk3dir/glsl/defaultskin.glsl index ed2b79cf..fa7b69e5 100644 --- a/base/resources.pk3dir/glsl/defaultskin.glsl +++ b/base/resources.pk3dir/glsl/defaultskin.glsl @@ -1,43 +1,64 @@ -!!ver 130 -!!permu FRAMEBLEND +//======= Copyright (c) 2015-2021 Vera Visions LLC. All rights reserved. ======= +// +// Purpose: +// +// Skinned objects, aka rigged objects are handled here. +// Skeletal operations are performed on the GPU (hopefully) and we don't care +// about lightmaps, but query the engine for a dir + ambient term which may +// come from the lightgrid or not exist at all. At that point it should be +// the rtlight shader doing the major work though. +//============================================================================== + +!!ver 100 150 + +!!permu FOG +!!permu BUMP +!!permu DELUXE +!!permu SPECULAR +!!permu FULLBRIGHT +!!permu FAKESHADOWS +!!permu OFFSETMAPPING !!permu SKELETAL !!permu UPPERLOWER -!!permu FOG -!!samps diffuse reflectcube upper lower -!!cvardf gl_affinemodels=0 -!!cvardf gl_ldr=1 -!!cvardf gl_halflambert=1 -!!cvardf gl_mono=0 -!!cvardf gl_kdither=0 -!!cvardf gl_stipplealpha=0 + +!!samps diffuse +!!samps =BUMP normalmap +!!samps =SPECULAR specular reflectcube +!!samps =FULLBRIGHT fullbright +!!samps =UPPERLOWER upper lower +!!samps =FAKESHADOWS shadowmap !!permu FAKESHADOWS !!cvardf r_glsl_pcf !!samps =FAKESHADOWS shadowmap -!!cvardf r_skipDiffuse +!!cvarf r_glsl_offsetmapping_scale +!!cvarf gl_specular + +#ifndef FRESNEL +#define FRESNEL 0.25f +#endif #include "sys/defs.h" -#include "sys/fog.h" -#if gl_affinemodels == 1 - #define affine noperspective -#else - #define affine -#endif +// always required +varying vec2 tc; +varying vec3 lightvector; +varying vec3 light; -#ifdef REFLECTCUBE +// from this point forth, if we check for SPECULAR this means we're in PBR territory +#ifdef BUMP varying vec3 eyevector; varying mat3 invsurface; +#define PBR #endif +// r_shadows 2 #ifdef FAKESHADOWS varying vec4 vtexprojcoord; #endif -affine varying vec2 tex_c; -varying vec3 light; - +// our basic vertex shader #ifdef VERTEX_SHADER #include "sys/skeletal.h" @@ -48,292 +69,145 @@ varying vec3 light; return ( dot( normal, dir ) * 0.5 ) + 0.5; } -#ifdef CHROME - /* Rotate Light Vector */ - vec3 rlv(vec3 axis, vec3 origin, vec3 lightpoint) - { - vec3 offs; - vec3 result; - offs[0] = lightpoint[0] - origin[0]; - offs[1] = lightpoint[1] - origin[1]; - offs[2] = lightpoint[2] - origin[2]; - result[0] = dot(offs[0], axis[0]); - result[1] = dot(offs[1], axis[1]); - result[2] = dot(offs[2], axis[2]); - return result; - } -#endif - - vec3 VectorIRotate( vec3 inPos, mat3x4 xform ) - { - vec3 outPos; - outPos.x = inPos.x*xform[0][0] + inPos.y*xform[1][0] + inPos.z*xform[2][0]; - outPos.y = inPos.x*xform[0][1] + inPos.y*xform[1][1] + inPos.z*xform[2][1]; - outPos.z = inPos.x*xform[0][2] + inPos.y*xform[1][2] + inPos.z*xform[2][2]; - return outPos; - } - - vec3 VectorTransform( vec3 inPos, mat3x4 xform ) - { - vec3 outPos; - outPos.x = dot( inPos, xform[0].xyz ) + xform[0][3]; - outPos.y = dot( inPos, xform[1].xyz ) + xform[1][3]; - outPos.z = dot( inPos, xform[2].xyz ) + xform[2][3]; - return outPos; - } - void main () { vec3 n, s, t, w; gl_Position = skeletaltransform_wnst(w,n,s,t); - tex_c = v_texcoord; - #if gl_halflambert==1 - light = e_light_ambient + (e_light_mul * halflambert(n, e_light_dir)); - #else + #ifdef PBR + vec3 eyeminusvertex = e_eyepos - w.xyz; + eyevector.x = dot(eyeminusvertex, s.xyz); + eyevector.y = dot(eyeminusvertex, t.xyz); + eyevector.z = dot(eyeminusvertex, n.xyz); + invsurface[0] = s; + invsurface[1] = t; + invsurface[2] = n; + #endif + light = e_light_ambient + (e_light_mul * lambert(n, e_light_dir)); + + tc = v_texcoord; + lightvector.x = dot(e_light_dir, s.xyz); + lightvector.y = dot(e_light_dir, t.xyz); + lightvector.z = dot(e_light_dir, n.xyz); + + #ifdef FAKESHADOWS + vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); #endif - - light *= e_lmscale.r; - - if (gl_ldr == 1.0) { - if (light.r > 1.5) - light.r = 1.5; - if (light.g > 1.5) - light.g = 1.5; - if (light.b > 1.5) - light.b = 1.5; - - light.rgb * 0.5; - light.rgb = floor(light.rgb * vec3(32,64,32))/vec3(32,64,32); - light.rgb * 2.0; - light.rgb *= 0.75; - } - -#ifdef CHROME - #ifndef SKELETAL - vec3 rorg = rlv(vec3(0,0,0), w, e_light_dir); - vec3 viewc = normalize(rorg - w); - float d = dot(n, viewc); - vec3 reflected; - reflected.x = n.x * 2.0 * d - viewc.x; - reflected.y = n.y * 2.0 * d - viewc.y; - reflected.z = n.z * 2.0 * d - viewc.z; - tex_c.x = 0.5 + reflected.y * 0.5; - tex_c.y = 0.5 - reflected.z * 0.5; - #else - /* code contributed by Slartibarty */ - vec3 tmp = e_eyepos * -1.0f; - - int boneid = int(v_bone.r); - tmp.x += m_bones_mat3x4[boneid][0][3]; - tmp.y += m_bones_mat3x4[boneid][1][3]; - tmp.z += m_bones_mat3x4[boneid][2][3]; - - tmp = normalize( tmp ); - vec3 chromeUp = normalize( cross( tmp, vec3( m_modelview[0][0], m_modelview[1][0], m_modelview[2][0] ) ) ); - vec3 chromeRight = normalize( cross( chromeUp, tmp ) ); - - chromeUp = VectorIRotate( chromeUp, m_bones_mat3x4[boneid] ); - chromeRight = VectorIRotate( chromeRight, m_bones_mat3x4[boneid] ); - - float na; - - // calc s coord - na = dot( v_normal, chromeRight ); - tex_c.x = ( na + 1.0 ) * 0.5; - - // calc t coord - na = dot( v_normal, chromeUp ); - tex_c.y = ( na + 1.0 ) * 0.5; - #endif -#endif - -#ifdef REFLECTCUBE - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); -#endif } #endif - #ifdef FRAGMENT_SHADER + #include "sys/fog.h" + + #if defined(SPECULAR) + uniform float cvar_gl_specular; + #endif + + #ifdef FAKESHADOWS #include "sys/pcf.h" + #endif - vec4 kernel_dither(sampler2D targ, vec2 texc) + #ifdef OFFSETMAPPING + #include "sys/offsetmapping.h" + #endif + + float LightingFuncGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0) { - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - int index = x + y * 2; - vec2 coord_ofs; - vec2 size; + float alpha = roughness*roughness; - size.x = 1.0 / textureSize(targ, 0).x; - size.y = 1.0 / textureSize(targ, 0).y; + vec3 H = normalize(V+L); - if (index == 0) - coord_ofs = vec2(0.25, 0.0); - else if (index == 1) - coord_ofs = vec2(0.50, 0.75); - else if (index == 2) - coord_ofs = vec2(0.75, 0.50); - else if (index == 3) - coord_ofs = vec2(0.00, 0.25); + float dotNL = clamp(dot(N,L), 0.0, 1.0); + float dotLH = clamp(dot(L,H), 0.0, 1.0); + float dotNH = clamp(dot(N,H), 0.0, 1.0); - return texture2D(targ, texc + coord_ofs * size); - } + float F, D, vis; - vec3 hsv2rgb(float h, float s, float v) - { - int i; - float f,p,q,t; - vec3 col = vec3(0,0,0); + // D + float alphaSqr = alpha*alpha; + float pi = 3.14159f; + float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f; + D = alphaSqr/(pi * denom * denom); - h = max(0.0, min(360.0, h)); - s = max(0.0, min(100.0, s)); - v = max(0.0, min(100.0, v)); + // F + float dotLH5 = pow(1.0f-dotLH,5); + F = F0 + (1.0-F0)*(dotLH5); - s /= 100; - v /= 100; + // V + float k = alpha/2.0f; + float k2 = k*k; + float invK2 = 1.0f-k2; + vis = 1.0/(dotLH*dotLH*invK2 + k2); - if (s == 0) { - col.x= col.y = col.z = int(v*255); - return col / 255.0; - } - - h /= 60; - i = int(floor(h)); - f = h - i; - p = v * (1 - s); - q = v * (1 - s * f); - t = v * (1 - s * (1 - f)); - - switch (i) { - case 0: - col[0] = int(255*v); - col[1] = int(255*t); - col[2] = int(255*p); - break; - case 1: - col[0] = int(255*q); - col[1] = int(255*v); - col[2] = int(255*p); - break; - case 2: - col[0] = int(255*p); - col[1] = int(255*v); - col[2] = int(255*t); - break; - case 3: - col[0] = int(255*p); - col[1] = int(255*q); - col[2] = int(255*v); - break; - case 4: - col[0] = int(255*t); - col[1] = int(255*p); - col[2] = int(255*v); - break; - default: - col[0] = int(255*v); - col[1] = int(255*p); - col[2] = int(255*q); - } - return col / 255.0; + float specular = dotNL * D * F * vis; + return specular; } void main () { - vec4 diffuse_f; - -#if r_skipDiffuse==1 - diffuse_f = vec4(1.0, 1.0, 1.0, 1.0); -#else - #if gl_kdither==1 - diffuse_f = kernel_dither(s_diffuse, tex_c); - #else - diffuse_f = texture2D(s_diffuse, tex_c); + #ifdef OFFSETMAPPING + vec2 tcoffsetmap = offsetmap(s_normalmap, tc, eyevector); + #define tc tcoffsetmap + #endif + + vec4 albedo_f = texture2D(s_diffuse, tc); + + #ifdef BUMP + vec3 normal_f = normalize(texture2D(s_normalmap, tc).rgb - 0.5); + #else + vec3 normal_f = vec3(0.0, 0.0, 1.0); #endif -#endif #ifdef UPPER - vec4 uc = texture2D(s_upper, tex_c); - - if (e_colourident.z == 2.0) { - vec3 topcolor = hsv2rgb(e_colourident.x * 360, 100, 100); - diffuse_f.rgb += uc.rgb*topcolor*uc.a; - } else { - diffuse_f.rgb += uc.rgb*e_uppercolour*uc.a; - } + vec4 uc = texture2D(s_upper, tc); + albedo_f.rgb += uc.rgb * e_uppercolour * uc.a; #endif #ifdef LOWER - vec4 lc = texture2D(s_lower, tex_c); - - if (e_colourident.z == 2.0) { - vec3 bottomcolor = hsv2rgb(e_colourident.y * 360, 100, 100); - diffuse_f.rgb += lc.rgb*bottomcolor*lc.a; - } else { - diffuse_f.rgb += lc.rgb*e_lowercolour*lc.a; - } + vec4 lc = texture2D(s_lower, tc); + albedo_f.rgb += lc.rgb * e_lowercolour * lc.a; #endif - diffuse_f.rgb *= light; + #ifdef PBR + float metalness_f = texture2D(s_specular, tc).r; + float roughness_f = texture2D(s_specular, tc).g; + float ao = texture2D(s_specular, tc).b; -#ifdef REFLECTCUBE + /* coords */ vec3 cube_c; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - cube_c = reflect( normalize( -eyevector ), vec3( 0, 0, 1 ) ); + /* calculate cubemap texcoords */ + cube_c = reflect(-normalize(eyevector), normal_f.rgb); cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = ( m_model * vec4( cube_c.xyz, 0.0 ) ).xyz; - out_f.rgb = mix( textureCube( s_reflectcube, cube_c ).rgb, diffuse_f.rgb, diffuse_f.a ); - diffuse_f = out_f; -#endif + cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - if (e_colourident.z != 2.0) { - diffuse_f *= e_colourident; - } + /* do PBR reflection using cubemap */ + gl_FragColor = albedo_f + (metalness_f * textureCube(s_reflectcube, cube_c)); - #if gl_stipplealpha==1 - float alpha = e_colourident.a; - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - - if (alpha <= 0.0) { - discard; - } else if (alpha <= 0.25) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 1) - discard; - } else if (alpha <= 0.5) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 0) - discard; - } else if (alpha < 1.0) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - } + /* do PBR specular using our handy function */ + gl_FragColor += (LightingFuncGGX(normal_f, normalize(eyevector), normalize(lightvector), roughness_f, FRESNEL) * gl_FragColor); + #else + gl_FragColor = albedo_f; #endif - #if gl_mono==1 - float bw = (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - diffuse_f.rgb = vec3(bw, bw, bw); - #endif + /* this isn't necessary if we're not doing lightgrid terms */ + gl_FragColor.rgb *= light; + /* r_shadows 2 */ #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); + gl_FragColor.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); #endif - gl_FragColor = fog4(diffuse_f); + + #ifdef PBR + gl_FragColor.rgb *= ao; + #endif + + #ifdef FULLBRIGHT + vec4 fb = texture2D(s_fullbright, tc); + gl_FragColor.rgb += fb.rgb * fb.a * e_glowmod.rgb; + #endif + + gl_FragColor = fog4(gl_FragColor * e_colourident); } #endif diff --git a/base/resources.pk3dir/glsl/defaultskybox.glsl b/base/resources.pk3dir/glsl/defaultskybox.glsl deleted file mode 100644 index 144e75a2..00000000 --- a/base/resources.pk3dir/glsl/defaultskybox.glsl +++ /dev/null @@ -1,30 +0,0 @@ -!!ver 130 -!!permu FOG -!!samps reflectcube -!!cvardf gl_mono=0 - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec3 pos; -#ifdef VERTEX_SHADER -void main () -{ - pos = v_position.xyz - e_eyepos; - pos.y = -pos.y; - gl_Position = ftetransform(); -} -#endif -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 skybox = textureCube(s_reflectcube, pos); - - if (gl_mono == 1.0) { - float bw = (skybox.r + skybox.g + skybox.b) / 3.0; - skybox.rgb = vec3(bw, bw, bw) * 1.5; - } - - gl_FragColor = vec4(fog3(skybox.rgb), 1.0); -} -#endif diff --git a/base/resources.pk3dir/glsl/defaultsprite.glsl b/base/resources.pk3dir/glsl/defaultsprite.glsl deleted file mode 100644 index 2ee22702..00000000 --- a/base/resources.pk3dir/glsl/defaultsprite.glsl +++ /dev/null @@ -1,74 +0,0 @@ -!!ver 130 -!!permu FOG -!!samps 1 -!!cvardf gl_mono=0 -!!cvardf gl_kdither=0 - -#include "sys/fog.h" -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; -varying vec2 tc; -varying vec4 vc; -void main () -{ - tc = v_texcoord; - vc = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -varying vec2 tc; -varying vec4 vc; -uniform vec4 e_colourident; -uniform vec4 e_vlscale; - - vec4 kernel_dither(sampler2D targ, vec2 texc) - { - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - int index = x + y * 2; - vec2 coord_ofs; - vec2 size; - - size.x = 1.0 / textureSize(targ, 0).x; - size.y = 1.0 / textureSize(targ, 0).y; - - if (index == 0) - coord_ofs = vec2(0.25, 0.0); - else if (index == 1) - coord_ofs = vec2(0.50, 0.75); - else if (index == 2) - coord_ofs = vec2(0.75, 0.50); - else if (index == 3) - coord_ofs = vec2(0.00, 0.25); - - return texture2D(targ, texc + coord_ofs * size); - } - - void main () - { - vec4 col; - - #if gl_kdither==1 - col = texture2D(s_t0, tc); - #else - col = texture2D(s_t0, tc); - #endif - - #ifdef MASK - if (col.a < float(MASK)) - discard; - #endif - - col = fog4blend(col * vc * e_colourident * e_vlscale); - - #if gl_mono==1 - float bw = (col.r + col.g + col.b) / 3.0; - col.rgb = vec3(bw, bw, bw) * 1.5; - #endif - - gl_FragColor = col; - } -#endif diff --git a/base/resources.pk3dir/glsl/defaultwall.glsl b/base/resources.pk3dir/glsl/defaultwall.glsl index f3beb2ff..28760d3b 100644 --- a/base/resources.pk3dir/glsl/defaultwall.glsl +++ b/base/resources.pk3dir/glsl/defaultwall.glsl @@ -1,37 +1,56 @@ -!!ver 130 -!!permu LIGHTSTYLED -!!permu FOG -!!samps diffuse reflectcube normalmap +//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= +// +// Purpose: +// +// Lightmapped surface that contains an environment cube as a reflection. +// Alpha channel of the diffuse decides reflectivity. +//============================================================================== +!!ver 100 150 + +!!permu FOG +!!permu BUMP +!!permu DELUXE +!!permu SPECULAR +!!permu FULLBRIGHT !!permu FAKESHADOWS -!!cvardf r_glsl_pcf +!!permu OFFSETMAPPING + +!!samps diffuse lightmap +!!samps =BUMP normalmap +!!samps =DELUXE deluxemap +!!samps =SPECULAR specular reflectcube +!!samps =FULLBRIGHT fullbright +!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 +!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 !!samps =FAKESHADOWS shadowmap -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!cvardf gl_mono -!!cvardf gl_kdither -!!cvardf gl_stipplealpha -!!cvardf gl_ldr - +!!cvardf r_glsl_pcf +!!cvarf r_glsl_offsetmapping_scale !!cvardf r_skipDiffuse +!!cvardf r_skipNormal +!!cvardf r_skipSpecular !!cvardf r_skipLightmap +#ifndef FRESNEL +#define FRESNEL 0.25f +#endif + #include "sys/defs.h" -#include "sys/fog.h" varying vec2 tex_c; +#ifdef BUMP +varying vec3 eyevector; +varying mat3 invsurface; +#define PBR +#endif + varying vec2 lm0; #ifdef LIGHTSTYLED varying vec2 lm1, lm2, lm3; #endif -#ifdef REFLECTCUBE -varying vec3 eyevector; -varying mat3 invsurface; -#endif - #ifdef FAKESHADOWS varying vec4 vtexprojcoord; #endif @@ -47,179 +66,183 @@ varying mat3 invsurface; #endif } - void main () + void main (void) { lightmapped_init(); - tex_c = v_texcoord; - gl_Position = ftetransform(); - /* HACK: func_conveyor needs us to scroll this surface! */ - if (e_glowmod.g == 0.5) - tex_c[0] += (e_time * (e_glowmod.b * 1024.0)) * -0.01; - -#ifdef REFLECTCUBE + #ifdef PBR invsurface[0] = v_svector; invsurface[1] = v_tvector; invsurface[2] = v_normal; vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); -#endif + eyevector.x = dot(eyeminusvertex, v_svector.xyz); + eyevector.y = dot(eyeminusvertex, v_tvector.xyz); + eyevector.z = dot(eyeminusvertex, v_normal.xyz); + #endif + + tex_c = v_texcoord; + gl_Position = ftetransform(); + + #ifdef FAKESHADOWS + vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); + #endif } #endif #ifdef FRAGMENT_SHADER + #include "sys/fog.h" #include "sys/pcf.h" + #ifdef OFFSETMAPPING + #include "sys/offsetmapping.h" + #endif + #if r_skipLightmap==0 - vec3 lightmap_fragment(void) + #ifdef LIGHTSTYLED + #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb + #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb + #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb + #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb + #else + #define LIGHTMAP texture2D(s_lightmap, lm0).rgb + #endif +#else + #ifdef LIGHTSTYLED + #define LIGHTMAP0 vec3(0.5,0.5,0.5) + #define LIGHTMAP1 vec3(0.5,0.5,0.5) + #define LIGHTMAP2 vec3(0.5,0.5,0.5) + #define LIGHTMAP3 vec3(0.5,0.5,0.5) + #else + #define LIGHTMAP vec3(0.5,0.5,0.5) + #endif +#endif + + float LightingFuncGGX(vec3 N, vec3 V, vec3 L, float roughness, float F0) + { + float alpha = roughness*roughness; + + vec3 H = normalize(V+L); + + float dotNL = clamp(dot(N,L), 0.0, 1.0); + float dotLH = clamp(dot(L,H), 0.0, 1.0); + float dotNH = clamp(dot(N,H), 0.0, 1.0); + + float F, D, vis; + + // D + float alphaSqr = alpha*alpha; + float pi = 3.14159f; + float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0f; + D = alphaSqr/(pi * denom * denom); + + // F + float dotLH5 = pow(1.0f-dotLH,5); + F = F0 + (1.0-F0)*(dotLH5); + + // V + float k = alpha/2.0f; + float k2 = k*k; + float invK2 = 1.0f-k2; + vis = 1.0/(dotLH*dotLH*invK2 + k2); + + float specular = dotNL * D * F * vis; + return specular; + } + + vec3 lightmap_fragment() { vec3 lightmaps; -#ifdef LIGHTSTYLED - lightmaps = texture2D(s_lightmap0, lm0).rgb * e_lmscale[0].rgb; - lightmaps += texture2D(s_lightmap1, lm1).rgb * e_lmscale[1].rgb; - lightmaps += texture2D(s_lightmap2, lm2).rgb * e_lmscale[2].rgb; - lightmaps += texture2D(s_lightmap3, lm3).rgb * e_lmscale[3].rgb; -#else - lightmaps = texture2D(s_lightmap, lm0).rgb * e_lmscale.rgb; -#endif - if (gl_ldr == 1.0) { - - if (lightmaps.r > 1.5) - lightmaps.r = 1.5; - if (lightmaps.g > 1.5) - lightmaps.g = 1.5; - if (lightmaps.b > 1.5) - lightmaps.b = 1.5; - - lightmaps.rgb * 0.5; - lightmaps.rgb = floor(lightmaps.rgb * vec3(32,64,32))/vec3(32,64,32); - lightmaps.rgb * 2.0; - } - + #ifdef LIGHTSTYLED + lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; + lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; + lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; + lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; + #else + lightmaps = LIGHTMAP * e_lmscale.rgb; + #endif return lightmaps; } -#else - vec3 lightmap_fragment(void) + +#if r_skipNormal==0 + vec3 lightmap_fragment(vec3 normal_f) { - return vec3(1.0,1.0,1.0); + #ifndef DELUXE + return lightmap_fragment(); + #else + vec3 lightmaps; + + #ifdef LIGHTSTYLED + lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, texture2D(s_deluxemap0, lm0).rgb); + lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, texture2D(s_deluxemap1, lm1).rgb); + lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, texture2D(s_deluxemap2, lm2).rgb); + lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, texture2D(s_deluxemap3, lm3).rgb); + #else + lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, texture2D(s_deluxemap, lm0).rgb); + #endif + + return lightmaps; + #endif } #endif - vec4 kernel_dither(sampler2D targ, vec2 texc) + void main (void) { - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - int index = x + y * 2; - vec2 coord_ofs; - vec2 size; - - size.x = 1.0 / textureSize(targ, 0).x; - size.y = 1.0 / textureSize(targ, 0).y; - - if (index == 0) - coord_ofs = vec2(0.25, 0.0); - else if (index == 1) - coord_ofs = vec2(0.50, 0.75); - else if (index == 2) - coord_ofs = vec2(0.75, 0.50); - else if (index == 3) - coord_ofs = vec2(0.00, 0.25); - - return texture2D(targ, texc + coord_ofs * size); - } - - void main ( void ) - { - vec4 diffuse_f; - -#if r_skipDiffuse==1 - diffuse_f = vec4(1.0,1.0,1.0,1.0); -#else - #if gl_kdither==1 - diffuse_f = kernel_dither(s_diffuse, tex_c); + #ifdef OFFSETMAPPING + vec2 tcoffsetmap = offsetmap(s_normalmap, tex_c, eyevector); #else - diffuse_f = texture2D(s_diffuse, tex_c); + #define tcoffsetmap tex_c #endif -#endif -/* get the alphatesting out of the way first */ -#ifdef MASK - /* HACK: terrible hack, CSQC sets this to mark surface as an entity - only entities are alphatested - ever */ - if (e_glowmod.r == 0.5) - if (diffuse_f.a < 0.6) { - discard; - } -#endif - /* lighting */ - //diffuse_f.rgb = vec3(1,1,1); - diffuse_f.rgb *= lightmap_fragment(); + /* samplers */ + vec4 albedo_f = texture2D(s_diffuse, tcoffsetmap); // diffuse RGBA + vec3 normal_f = normalize(texture2D(s_normalmap, tcoffsetmap).rgb - 0.5); // normalmap RGB -#ifdef REFLECTCUBE - #ifdef BUMP - #ifndef FLATTENNORM - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #else - // For very flat surfaces and gentle surface distortions, the 8-bit precision per channel in the normalmap - // can be insufficient. This is a hack to instead have very wobbly normalmaps that make use of the 8 bits - // and then scale the wobblyness back once in the floating-point domain. - vec3 normal_f = texture2D(s_normalmap, tex_c).rgb - 0.5; - normal_f.x *= 0.0625; - normal_f.y *= 0.0625; - normal_f = normalize(normal_f); - #endif - #else - vec3 normal_f = vec3(0, 0, 1); - #endif + /* deluxe/light */ + vec3 deluxe = normalize(texture2D(s_deluxemap, lm0).rgb); + + #ifdef PBR + float metalness_f =texture2D(s_specular, tcoffsetmap).r; // specularmap R + float roughness_f = texture2D(s_specular, tcoffsetmap).g; // specularmap G + float ao = texture2D(s_specular, tcoffsetmap).b; // specularmap B + + /* coords */ vec3 cube_c; - cube_c = reflect( normalize(-eyevector), normal_f); + /* calculate cubemap texcoords */ + cube_c = reflect(-normalize(eyevector), normal_f.rgb); cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = ( m_model * vec4(cube_c.xyz, 0.0)).xyz; - diffuse_f.rgb = mix( textureCube(s_reflectcube, cube_c ).rgb, diffuse_f.rgb, diffuse_f.a); -#endif + cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - diffuse_f *= e_colourident; + /* do PBR reflection using cubemap */ + gl_FragColor = albedo_f + (metalness_f * textureCube(s_reflectcube, cube_c)); - #if gl_stipplealpha==1 - float alpha = e_colourident.a; - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - - if (alpha <= 0.0) { - discard; - } else if (alpha <= 0.25) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 1) - discard; - } else if (alpha <= 0.5) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 0) - discard; - } else if (alpha < 1.0) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - } + /* do PBR specular using our handy function */ + gl_FragColor += (LightingFuncGGX(normal_f, normalize(eyevector), deluxe, roughness_f, FRESNEL) * gl_FragColor); + #else + gl_FragColor = albedo_f; #endif - #if gl_mono==1 - float bw = (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - diffuse_f.rgb = vec3(bw, bw, bw); - #endif + /* calculate lightmap fragment on top */ + gl_FragColor.rgb *= lightmap_fragment(normal_f); + /* r_shadows 2 */ #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); + gl_FragColor.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); #endif - gl_FragColor = fog4(diffuse_f); - + /* emissive texture/fullbright bits */ + #ifdef FULLBRIGHT + vec3 emission_f = texture2D(s_fullbright, tcoffsetmap).rgb; // fullbrightmap RGB + gl_FragColor.rgb += emission_f; + #endif + + /* ambient occlusion */ + #ifdef PBR + gl_FragColor.rgb *= ao; + #endif + + /* and let the engine add fog on top */ + gl_FragColor = fog4(gl_FragColor); } #endif diff --git a/base/resources.pk3dir/glsl/defaultwarp.glsl b/base/resources.pk3dir/glsl/defaultwarp.glsl deleted file mode 100644 index 0770d245..00000000 --- a/base/resources.pk3dir/glsl/defaultwarp.glsl +++ /dev/null @@ -1,146 +0,0 @@ -!!ver 100 450 -!!permu FOG -!!samps diffuse lightmap -!!cvardf gl_mono=0 -!!cvardf gl_stipplealpha=0 -!!cvardf r_waterRipples=0 - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tc; - -#ifdef LIT -varying vec2 lm0; -#endif - -#ifdef VERTEX_SHADER - void main () - { - tc = v_texcoord.st; - #ifdef FLOW - tc.s += e_time * -0.5; - #endif - #ifdef LIT - lm0 = v_lmcoord; - #endif - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER -#ifndef ALPHA - #define USEALPHA 1.0 -#else - #define USEALPHA float(ALPHA) -#endif - - - -// Hash functions shamefully stolen from: -// https://www.shadertoy.com/view/4djSRW -#define HASHSCALE1 .1031 -#define HASHSCALE3 vec3(.1031, .1030, .0973) - -float hash12(vec2 p) -{ - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE1); - p3 += dot(p3, p3.yzx + 19.19); - return fract((p3.x + p3.y) * p3.z); -} - -vec2 hash22(vec2 p) -{ - vec3 p3 = fract(vec3(p.xyx) * HASHSCALE3); - p3 += dot(p3, p3.yzx+19.19); - return fract((p3.xx+p3.yz)*p3.zy); - -} - - void main () - { - vec2 ntc; - ntc.s = tc.s + sin(tc.t+ e_time)*0.125; - ntc.t = tc.t + sin(tc.s+ e_time)*0.125; - vec4 diffuse_f = texture2D(s_diffuse, ntc); - - diffuse_f *= e_colourident; - - // awful stipple alpha code - #if gl_stipplealpha==1 - float alpha = USEALPHA * e_colourident.a; - int x = int(mod(gl_FragCoord.x, 2.0)); - int y = int(mod(gl_FragCoord.y, 2.0)); - - if (alpha <= 0.0) { - discard; - } else if (alpha <= 0.25) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 1) - discard; - } else if (alpha <= 0.5) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - if (x + y == 0) - discard; - } else if (alpha < 1.0) { - diffuse_f.a = 1.0; - if (x + y == 2) - discard; - } - #else - #ifdef LIT - - #define MAX_RADIUS 2 - - #if r_waterRipples ==1 - float resolution = 5.0f; - float riptime = e_time; - vec2 uv = tc.xy * resolution; - vec2 p0 = floor(uv); - vec2 circles = vec2(0.); - for (int j = -MAX_RADIUS; j <= MAX_RADIUS; ++j) { - for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i) { - vec2 pi = p0 + vec2(i, j); - #if DOUBLE_HASH - vec2 hsh = hash22(pi); - #else - vec2 hsh = pi; - #endif - vec2 p = pi + hash22(hsh); - - float t = fract(0.3* riptime + hash12(hsh)); - vec2 v = p - uv; - float d = length(v) - (float(MAX_RADIUS) + 1.)*t; - - float h = 1e-3; - float d1 = d - h; - float d2 = d + h; - float p1 = sin(31.*d1) * smoothstep(-0.6, -0.3, d1) * smoothstep(0., -0.3, d1); - float p2 = sin(31.*d2) * smoothstep(-0.6, -0.3, d2) * smoothstep(0., -0.3, d2); - circles += .05 * normalize(v) * ((p2 - p1) / (2. * h) * (1. - t) * (1. - t)); - } - } - circles /= float((MAX_RADIUS*2+1)*(MAX_RADIUS*2+1)); - - float intensity = mix(0.01, 0.15, smoothstep(0.1, 0.6, abs(fract(0.05* riptime + 0.5)*2.-1.))); - vec3 n = vec3(circles, sqrt(1. - dot(circles, circles))); - - diffuse_f = texture2D(s_diffuse, tc + n.yz); - #endif - //diffuse_f.rgb += pow(clamp(dot(n, normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.); - diffuse_f.rgb *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb; - #endif - #endif - - #if gl_mono==1 - float bw = (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - diffuse_f.rgb = vec3(bw, bw, bw); - #endif - - gl_FragColor = fog4(diffuse_f); - } -#endif diff --git a/base/resources.pk3dir/glsl/depthonly.glsl b/base/resources.pk3dir/glsl/depthonly.glsl deleted file mode 100644 index 99f4db6d..00000000 --- a/base/resources.pk3dir/glsl/depthonly.glsl +++ /dev/null @@ -1,29 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// None. -//============================================================================== - -!!ver 110 -!!permu FRAMEBLEND -!!permu SKELETAL - -#include "sys/defs.h" - -#ifdef VERTEX_SHADER - #include "sys/skeletal.h" - - void main () - { - gl_Position = skeletaltransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main () - { - gl_FragColor = vec4(0, 0, 0, 1); - } -#endif - diff --git a/base/resources.pk3dir/glsl/fade.glsl b/base/resources.pk3dir/glsl/fade.glsl deleted file mode 100644 index 931308fb..00000000 --- a/base/resources.pk3dir/glsl/fade.glsl +++ /dev/null @@ -1,44 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Fades surfaces in with distance. It's the opposite of the clutter shader. -//============================================================================== - -!!ver 110 -!!samps diffuse=0 - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; -varying float eyedist; - -#ifndef TINT -#define TINT 1.0,1.0,1.0 -#endif - -#ifdef VERTEX_SHADER -void main () -{ - tex_c = v_texcoord; - eyedist = abs( length( e_eyepos - v_position.xyz ) ) / 1024.0; - - if (eyedist > 1.0) { - eyedist = 1.0; - } else if (eyedist < 0.0) { - eyedist = 0.0; - } - - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - gl_FragColor = vec4( texture2D( s_diffuse, tex_c ).rgb * eyedist, eyedist ); - gl_FragColor *= e_colourident; - gl_FragColor.rgb *= vec3(TINT); -} -#endif diff --git a/base/resources.pk3dir/glsl/fill.glsl b/base/resources.pk3dir/glsl/fill.glsl deleted file mode 100644 index 876dba04..00000000 --- a/base/resources.pk3dir/glsl/fill.glsl +++ /dev/null @@ -1,33 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Fills any surface with a diffuse texture and applies vertex colors. -//============================================================================== - -!!ver 110 -!!samps tex=0 - -varying vec2 tc; -varying vec4 vc; - -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; -void main () -{ - tc = v_texcoord; - vc = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 f = vc; - f.rgb *= f.a; - f *= texture2D(s_tex, tc); - gl_FragColor = f; -} -#endif diff --git a/base/resources.pk3dir/glsl/fullbright_reflect.glsl b/base/resources.pk3dir/glsl/fullbright_reflect.glsl deleted file mode 100644 index fdf4a5eb..00000000 --- a/base/resources.pk3dir/glsl/fullbright_reflect.glsl +++ /dev/null @@ -1,62 +0,0 @@ -!!ver 110 -!!samps diffuse reflectcube normalmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; -varying mat3 invsurface; - -#ifdef VERTEX_SHADER - void main () - { - tex_c = v_texcoord; - gl_Position = ftetransform(); - - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); - } -#endif - -#ifdef FRAGMENT_SHADER - void main ( void ) - { - vec4 diffuse_f = texture2D(s_diffuse, tex_c); - - if (diffuse_f.rgb == vec3(0,0,1)) { - diffuse_f.rgb = vec3(0,0,0); - discard; - } - - #ifdef BUMP - #ifndef FLATTENNORM - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #else - // For very flat surfaces and gentle surface distortions, the 8-bit precision per channel in the normalmap - // can be insufficient. This is a hack to instead have very wobbly normalmaps that make use of the 8 bits - // and then scale the wobblyness back once in the floating-point domain. - vec3 normal_f = texture2D(s_normalmap, tex_c).rgb - 0.5; - normal_f.x *= 0.0625; - normal_f.y *= 0.0625; - normal_f = normalize(normal_f); - #endif - #else - vec3 normal_f = vec3(0, 0, 1); - #endif - vec3 cube_c; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - - cube_c = reflect( normalize(-eyevector), normal_f); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = ( m_model * vec4(cube_c.xyz, 0.0)).xyz; - out_f.rgb = mix( textureCube(s_reflectcube, cube_c ).rgb, diffuse_f.rgb, diffuse_f.a); - diffuse_f = out_f * e_colourident; - - gl_FragColor = diffuse_f; - } -#endif diff --git a/base/resources.pk3dir/glsl/heat.glsl b/base/resources.pk3dir/glsl/heat.glsl deleted file mode 100644 index db989367..00000000 --- a/base/resources.pk3dir/glsl/heat.glsl +++ /dev/null @@ -1,56 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Non-lit surface that predominantly offers environment cube reflection -// modulated by the diffusemap's RGB. This is used for glass, for example. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffusemap=0 normalmap=1 reflect=2 - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; -varying mat3 invsurface; -varying vec4 tf; - -#ifdef VERTEX_SHADER -void main () -{ - - tex_c = v_texcoord; - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); - tf = ftetransform(); - gl_Position = tf; -} -#endif - -#ifdef FRAGMENT_SHADER -#include "sys/fog.h" -void main () -{ - vec2 stc; - vec3 out_f; - vec4 diffuse_f = texture2D(s_diffusemap, tex_c); - vec3 norm_f; - - norm_f = ( texture2D( s_normalmap, tex_c + vec2( e_time * 0.01, 0.0 ) ).xyz); - norm_f += ( texture2D( s_normalmap, tex_c - vec2( 0, e_time * 0.01 ) ).xyz); - norm_f -= 1.0 - ( 4.0 / 256.0 ); - norm_f = normalize( norm_f ); - - stc = (1.0 + (tf.xy / tf.w)) * 0.5; - - diffuse_f.rgb = texture2D(s_reflect, stc + norm_f.st).rgb; - gl_FragColor = diffuse_f; -} -#endif diff --git a/base/resources.pk3dir/glsl/lensflare.glsl b/base/resources.pk3dir/glsl/lensflare.glsl deleted file mode 100644 index ac650c41..00000000 --- a/base/resources.pk3dir/glsl/lensflare.glsl +++ /dev/null @@ -1,33 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Shader used for fading out surfaces after a certain distance. -// It only has a diffuse map. -//============================================================================== - -!!ver 110 -!!samps diffuse=0 - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER -void main () -{ - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 diffuse_f = texture2D(s_diffuse, tex_c) * vex_color.a; - gl_FragColor = diffuse_f; -} -#endif diff --git a/base/resources.pk3dir/glsl/lightmapped.glsl b/base/resources.pk3dir/glsl/lightmapped.glsl deleted file mode 100644 index abbff2fa..00000000 --- a/base/resources.pk3dir/glsl/lightmapped.glsl +++ /dev/null @@ -1,245 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightmapped surface. -// -// diffusemap = albedo (rgba) -// normalmap = normal (rgb), reflectmask (a) -//============================================================================== - -!!ver 110 -!!permu FOG - -!!permu BUMP -!!permu DELUXE -!!permu LIGHTSTYLED -!!permu FULLBRIGHT -!!permu UPPERLOWER -!!samps diffuse - -!!samps lightmap -!!samps =BUMP normalmap reflectcube -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!samps =FULLBRIGHT fullbright -!!samps =UPPERLOWER upper - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!cvardf r_fullbright -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipFullbright -!!cvardf r_skipNormal -!!cvardf r_skipEnvmap -!!cvardf r_skipLightmap -!!cvardf r_skipDetail - -#include "sys/defs.h" - -// basics -varying vec2 tex_c; -varying vec2 lm0; - -// unfortunately we do support lightstyles -#if defined(LIGHTSTYLED) -varying vec2 lm1, lm2, lm3; -#endif - -// useful for terrain blending -varying vec4 vex_color; - -// dynamic shadows -#ifdef FAKESHADOWS -varying vec4 vtexprojcoord; -#endif - -#ifdef BUMP -varying vec3 eyevector; -varying mat3 invsurface; -#endif - -varying vec3 norm; - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #if defined(LIGHTSTYLED) - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main () - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - - #ifdef BUMP - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot(eyeminusvertex, v_svector.xyz); - eyevector.y = dot(eyeminusvertex, v_tvector.xyz); - eyevector.z = dot(eyeminusvertex, v_normal.xyz); - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - #endif - norm = v_normal; - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - - #if defined(LIGHTSTYLED) - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif - -#if r_skipLightmap == 0 - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; - } - - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, texture2D(s_deluxemap0, lm0).rgb); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, texture2D(s_deluxemap1, lm1).rgb); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, texture2D(s_deluxemap2, lm2).rgb); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, texture2D(s_deluxemap3, lm3).rgb); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, texture2D(s_deluxemap, lm0).rgb); - #endif - - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; -#endif - } -#else - vec3 lightmap_fragment() - { - return vec3(1.0,1.0,1.0); - } - vec3 lightmap_fragment(vec3 normal_f) - { - return vec3(1.0,1.0,1.0); - } -#endif - - float lambert(vec3 normal, vec3 dir) - { - return max(dot(normal, dir), 0.0); - } - - void main (void) - { - vec4 diffuse_f; - float alpha; - - #if r_skipDiffuse == 0 - diffuse_f = texture2D(s_diffuse, tex_c); - #else - diffuse_f = vec4(1.0, 1.0, 1.0, 1.0); - #endif - -#ifdef MASK - // alpha-testing happens here - if (diffuse_f.a < MASK) - discard; -#endif - - #if r_skipDetail == 0 - #if defined(UPPERLOWER) - diffuse_f.rgb *= (texture2D(s_upper, tex_c * 4.0).rgb + 0.5); - #endif - #endif - - #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - // the lighting stage for the world - #if defined(BUMP) - float refl = texture2D(s_normalmap, tex_c).a; - - // whether to respect our bump, or to act flat - #if r_skipNormal == 0 - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - diffuse_f.rgb *= lightmap_fragment(normal_f); - #else - vec3 normal_f = vec3(0.0, 0.0, 1.0); - diffuse_f.rgb *= lightmap_fragment(); - #endif - - // environment mapping happens here - #if r_skipEnvmap == 0 - vec3 cube_c; - vec3 env_f; - cube_c = reflect(normalize(-eyevector), vec3(0.0, 0.0, 1.0)); - cube_c = cube_c.x * invsurface[0] + - cube_c.y * invsurface[1] + - cube_c.z * invsurface[2]; - cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - env_f = textureCube(s_reflectcube, cube_c).rgb * (e_lmscale.rgb * 0.25); - diffuse_f.rgb = mix(env_f, diffuse_f.rgb, refl); - #else - diffuse_f.rgb = mix(vec3(0.0, 0.0, 0.0), diffuse_f.rgb, refl); - #endif - #else - diffuse_f.rgb *= lightmap_fragment(); - #endif - - #if defined(FULLBRIGHT) && r_skipFullbright == 0 - diffuse_f.rgb += texture2D(s_fullbright, tex_c).rgb; - #endif - - #if defined (VERTEXLIT) - vec3 light; - /* directional light */ - light = (vec3(0.5,0.5,0.5) * lambert(norm, vec3(-1,-0.5,0.5))) * 2.0; - light += (vec3(0.25,0.25,0.25) * lambert(norm, reflect(norm, vec3(0.75, 0, 0)))) * 0.5; - light *= 2.0; - diffuse_f.rgb = texture2D(s_diffuse, tex_c).rgb * light; - #endif - - // start blend at half-way point - alpha = vex_color.a * 1.5; - - if (alpha > 1.0) - alpha = 1.0; - - gl_FragColor = vec4(fog3(diffuse_f.rgb), alpha); - } -#endif diff --git a/base/resources.pk3dir/glsl/lightmapped_reflect.glsl b/base/resources.pk3dir/glsl/lightmapped_reflect.glsl deleted file mode 100644 index ba533a8e..00000000 --- a/base/resources.pk3dir/glsl/lightmapped_reflect.glsl +++ /dev/null @@ -1,167 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightmapped surface that effectively acts as a mirror. -// Alpha channel of the diffusemap is referenced for reflectivity. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!samps diffuse lightmap deluxemap normalmap -!!samps reflect=0 - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipNormal -!!cvardf r_skipLightmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying mat3 invsurface; -varying vec4 tf; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main (void) - { - lightmapped_init(); - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - - tf = ftetransform(); - tex_c = v_texcoord; - gl_Position = tf; - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - - #define s_reflect s_t0 - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, texture2D(s_deluxemap0, lm0).rgb); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, texture2D(s_deluxemap1, lm1).rgb); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, texture2D(s_deluxemap2, lm2).rgb); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, texture2D(s_deluxemap3, lm3).rgb ); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, texture2D(s_deluxemap, lm0).rgb); - #endif - - return lightmaps; -#endif - } -#endif - - void main (void) - { - vec2 stc; - vec4 diffuse_f; - - #if r_skipDiffuse == 0 - diffuse_f = texture2D(s_diffuse, tex_c); - #else - diffuse_f = vec4(1.0, 1.0, 1.0, 1.0); - #endif - - #if r_skipNormal==1 - #define normal_f vec3(0.0,0.0,0.5) - #else - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #endif - - float refl = texture2D(s_normalmap, tex_c).a; - - #if r_skipNormal==1 - diffuse_f.rgb *= lightmap_fragment(); - #else - diffuse_f.rgb *= lightmap_fragment(normal_f); - #endif - - /* map the reflection buffer onto the surface */ - stc = (1.0 + (tf.xy / tf.w)) * 0.5; - stc.t -= 1.5* invsurface[2].z / 1080.0; - - diffuse_f.rgb = mix(texture2D(s_reflect, stc).rgb, diffuse_f.rgb, refl); - - #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - gl_FragColor = fog4(diffuse_f); - } -#endif diff --git a/base/resources.pk3dir/glsl/lightmapped_specular.glsl b/base/resources.pk3dir/glsl/lightmapped_specular.glsl deleted file mode 100644 index a4e477e0..00000000 --- a/base/resources.pk3dir/glsl/lightmapped_specular.glsl +++ /dev/null @@ -1,166 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightmapped surface, normalmap's alpha contains the reference for how -// specular it should be. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!samps diffuse normalmap lightmap deluxemap - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipNormal -!!cvardf r_skipSpecular -!!cvardf r_skipLightmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main () - { - lightmapped_init(); - tex_c = v_texcoord; - - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot(eyeminusvertex, v_svector.xyz); - eyevector.y = dot(eyeminusvertex, v_tvector.xyz); - eyevector.z = dot(eyeminusvertex, v_normal.xyz); - - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, texture2D(s_deluxemap0, lm0).rgb); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, texture2D(s_deluxemap1, lm1).rgb); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, texture2D(s_deluxemap2, lm2).rgb); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, texture2D(s_deluxemap3, lm3).rgb); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, texture2D(s_deluxemap, lm0).rgb); - #endif - - return lightmaps; -#endif - } -#endif - - void main (void) - { - #if r_skipDiffuse==0 - vec4 diffuse_f = texture2D(s_diffuse, tex_c); - #else - vec4 diffuse_f = vec4(1.0,1.0,1.0,1.0); - #endif - - #if r_skipNormal==1 - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #else - #define normal_f vec3(0.0,0.0,0.5) - #endif - - if (diffuse_f.a < 0.5) { - discard; - } - - #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - #if r_skipNormal==1 - diffuse_f.rgb *= lightmap_fragment(); - #else - diffuse_f.rgb *= lightmap_fragment(normal_f); - #endif - - #if r_skipSpecular==0 - float gloss = texture2D(s_normalmap, tex_c).a * 0.1; - vec3 halfdir = normalize(normalize(eyevector) - e_light_dir); - float spec = pow(max(dot(halfdir, normal_f), 0.0), FTE_SPECULAR_EXPONENT); - spec *= gloss; - diffuse_f.rgb += spec; - #endif - - gl_FragColor = fog4(diffuse_f); - } -#endif diff --git a/base/resources.pk3dir/glsl/portal.glsl b/base/resources.pk3dir/glsl/portal.glsl deleted file mode 100644 index d6a5ec44..00000000 --- a/base/resources.pk3dir/glsl/portal.glsl +++ /dev/null @@ -1,36 +0,0 @@ -//======= Copyright (c) 2023 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Unlit surface. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffuse - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; - -#ifdef VERTEX_SHADER -void main () -{ - tex_c = v_texcoord; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 d_f = vec4(1.0, 1.0, 1.0, 1.0) - texture2D( s_diffuse, tex_c ); - - if (d_f.a > 0.5) { - discard; - } - - gl_FragColor = fog4( d_f ); -} -#endif diff --git a/base/resources.pk3dir/glsl/pp_colorcorrect.glsl b/base/resources.pk3dir/glsl/pp_colorcorrect.glsl deleted file mode 100644 index 5f226171..00000000 --- a/base/resources.pk3dir/glsl/pp_colorcorrect.glsl +++ /dev/null @@ -1,31 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Default post-processing shader for the framebuffer. -// Does a 'dodge' filter onto the final scene. -//============================================================================== - -!!samps screen=0 - -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -varying vec2 texcoord; - -void main() -{ - texcoord = v_texcoord.xy; - texcoord.y = 1.0 - texcoord.y; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -varying vec2 texcoord; -void main() -{ - vec4 colortint = vec4(COLOR); - vec3 dodged = vec3(1.0,1.0,1.0) - (colortint.rgb * colortint.a); - gl_FragColor.rgb = texture2D(s_screen, texcoord).rgb / dodged; -} -#endif diff --git a/base/resources.pk3dir/glsl/reflect.glsl b/base/resources.pk3dir/glsl/reflect.glsl deleted file mode 100644 index 8bdea399..00000000 --- a/base/resources.pk3dir/glsl/reflect.glsl +++ /dev/null @@ -1,53 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Non-lit surface that predominantly offers environment cube reflection -// modulated by the diffusemap's RGB. This is used for glass, for example. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffusemap=0 normalmap=1 cubemap:samplerCube=2 - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; -varying mat3 invsurface; - -#ifdef VERTEX_SHADER -void main () -{ - - tex_c = v_texcoord; - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - vec3 eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -#include "sys/fog.h" -void main () -{ - vec3 out_f; - vec3 diffuse_f = texture2D(s_diffusemap, tex_c).rgb; - vec3 normal_f = texture2D(s_normalmap, tex_c).rgb - 0.5; - normal_f.x *= 0.2; - normal_f.y *= 0.2; - normal_f = normalize(normal_f); - vec3 rtc = reflect( normalize( -eyevector ), normal_f ); - rtc = rtc.x * invsurface[0] + rtc.y * invsurface[1] + rtc.z * invsurface[2]; - rtc = ( m_model * vec4( rtc.xyz, 0.0 ) ).xyz; - - diffuse_f = textureCube(s_cubemap, rtc).rgb * (diffuse_f * 2.5); - - gl_FragColor = fog4(vec4( diffuse_f, 1.0 )); -} -#endif diff --git a/base/resources.pk3dir/glsl/refract.glsl b/base/resources.pk3dir/glsl/refract.glsl deleted file mode 100644 index 4aab4786..00000000 --- a/base/resources.pk3dir/glsl/refract.glsl +++ /dev/null @@ -1,45 +0,0 @@ -!!ver 110 -!!samps refraction=0 normalmap=1 - -#include "sys/defs.h" - -varying vec2 tex_c; -varying mat3 invsurface; -varying vec4 tf_c; -varying vec3 eyeminusvertex; - -#ifdef VERTEX_SHADER - void main () - { - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - tf_c = ftetransform(); - tex_c = v_texcoord; - gl_Position = tf_c; - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main ( void ) - { - vec2 refl_c; - vec3 refr_f; - vec3 norm_f; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - - norm_f = ( texture2D( s_normalmap, tex_c + vec2( e_time * 0.01, 0.0 ) ).xyz); - norm_f += ( texture2D( s_normalmap, tex_c - vec2( 0, e_time * 0.01 ) ).xyz); - norm_f -= 1.0 - ( 4.0 / 256.0 ); - norm_f = normalize( norm_f ); - - // Reflection/View coordinates - refl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5; - - refr_f = texture2D( s_refraction, refl_c + ( norm_f.st) ).rgb; - out_f.rgb = refr_f; - - gl_FragColor = out_f; - } -#endif diff --git a/base/resources.pk3dir/glsl/rtlight.glsl b/base/resources.pk3dir/glsl/rtlight.glsl index 13b57e4f..b33f38b1 100644 --- a/base/resources.pk3dir/glsl/rtlight.glsl +++ b/base/resources.pk3dir/glsl/rtlight.glsl @@ -1,181 +1,230 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= +//======= Copyright (c) 2015-2021 Vera Visions LLC. All rights reserved. ======= // // Purpose: // -// Code for all the dynamic light passes. The renderer is not aware of any -// surface properties beyond diffuse, normal and specularity. -// Alpha-masked surfaces suffer greatly because of this. -// -// diffusemap = albedo (rgba) -// normalmap = normal (rgb), reflectmask (a) +// Lightmapped surface that contains an environment cube as a reflection. +// Alpha channel of the diffuse decides reflectivity. //============================================================================== -!!ver 100 300 +!!ver 100 150 + !!permu BUMP !!permu FRAMEBLEND !!permu SKELETAL -!!permu FOG !!permu UPPERLOWER +!!permu FOG +!!permu REFLECTCUBEMASK +!!cvarf r_glsl_offsetmapping_scale !!cvardf r_glsl_pcf -!!samps diffuse -!!samps =BUMP normalmap reflectcube -!!samps =PCF shadowmap -!!samps =CUBE projectionmap -!!samps =UPPERLOWER upper +!!cvardf r_glsl_fresnel -!!cvardf r_skipDiffuse +!!samps diffuse shadowmap projectionmap +!!samps =BUMP normalmap +!!samps =UPPERLOWER upper lower +!!samps =SPECULAR specular reflectcube +!!samps =FAKESHADOWS shadowmap #include "sys/defs.h" -//if there's no vertex normals known, disable some stuff. -//FIXME: this results in dupe permutations. -#ifdef NOBUMP - #undef SPECULAR - #undef BUMP -#endif - -varying vec2 tex_c; +varying vec2 tcbase; varying vec3 lightvector; -#define VERTEXCOLOURS - -#if defined(VERTEXCOLOURS) - varying vec4 vc; +#ifdef VERTEXCOLOURS +varying vec4 vc; #endif -#ifdef BUMP - varying vec3 eyevector; - varying mat3 invsurface; +#ifdef SPECULAR +varying vec3 eyevector; +varying mat3 invsurface; +#define PBR #endif -#if defined(PCF) || defined(CUBE) || defined(SPOT) || defined(ORTHO) - varying vec4 vtexprojcoord; +#if defined(PCF) || defined(CUBE) || defined(SPOT) +varying vec4 vtexprojcoord; #endif #ifdef VERTEX_SHADER #include "sys/skeletal.h" - void main () { vec3 n, s, t, w; gl_Position = skeletaltransform_wnst(w,n,s,t); - n = normalize(n); - s = normalize(s); - t = normalize(t); - tex_c = v_texcoord; - #ifdef ORTHO - vec3 lightminusvertex = -l_lightdirection; + tcbase = v_texcoord; //pass the texture coords straight through + vec3 lightminusvertex = l_lightposition - w.xyz; + + #ifdef NOBUMP + //the only important thing is distance + lightvector = lightminusvertex; + #else + //the light direction relative to the surface normal, for bumpmapping. lightvector.x = dot(lightminusvertex, s.xyz); lightvector.y = dot(lightminusvertex, t.xyz); lightvector.z = dot(lightminusvertex, n.xyz); - #else - vec3 lightminusvertex = l_lightposition - w.xyz; - #ifdef NOBUMP - lightvector = lightminusvertex; - #else - // light direction relative to the surface normal, for bumpmapping. - lightvector.x = dot(lightminusvertex, s.xyz); - lightvector.y = dot(lightminusvertex, t.xyz); - lightvector.z = dot(lightminusvertex, n.xyz); - #endif #endif - #if defined(VERTEXCOLOURS) + #ifdef VERTEXCOLOURS vc = v_colour; #endif - #ifdef BUMP + #ifdef SPECULAR vec3 eyeminusvertex = e_eyepos - w.xyz; eyevector.x = dot(eyeminusvertex, s.xyz); eyevector.y = dot(eyeminusvertex, t.xyz); eyevector.z = dot(eyeminusvertex, n.xyz); - invsurface = mat3(v_svector, v_tvector, v_normal); + invsurface[0] = v_svector; + invsurface[1] = v_tvector; + invsurface[2] = v_normal; #endif - #if defined(PCF) || defined(SPOT) || defined(CUBE) || defined(ORTHO) + #if defined(PCF) || defined(SPOT) || defined(CUBE) //for texture projections/shadowmapping on dlights vtexprojcoord = (l_cubematrix*vec4(w.xyz, 1.0)); #endif -} + } #endif + #ifdef FRAGMENT_SHADER + vec3 LightingFuncShlick(vec3 N, vec3 V, vec3 L, float roughness, vec3 Cdiff, vec3 F0) + { + vec3 H = normalize(V+L); + float NL = clamp(dot(N,L), 0.001, 1.0); + float LH = clamp(dot(L,H), 0.0, 1.0); + float NH = clamp(dot(N,H), 0.0, 1.0); + float NV = clamp(abs(dot(N,V)), 0.001, 1.0); + float VH = clamp(dot(V,H), 0.0, 1.0); + float PI = 3.14159f; + + //Fresnel term + //the fresnel models glancing light. + //(Schlick) + vec3 F = F0 + (1.0-F0)*pow(1.0-VH, 5.0); + + //Schlick + float k = roughness*0.79788456080286535587989211986876; + float G = (LH/(LH*(1.0-k)+k)) * (NH/(NH*(1.0-k)+k)); + + //microfacet distribution + float a = roughness*roughness; + a *= a; + float t = (NH*NH*(a-1.0)+1.0); + + float D = a/(PI*t*t); + + if (r_glsl_fresnel == 1) + return vec3(F); + if (r_glsl_fresnel == 2) + return vec3(G); + if (r_glsl_fresnel == 3) + return vec3(D); + + return ((1.0-F)*(Cdiff/PI) + + (F*G*D)/(4*NL*NV)) * NL; + } + #include "sys/fog.h" #include "sys/pcf.h" + #ifdef OFFSETMAPPING + #include "sys/offsetmapping.h" + #endif + void main () { + + #ifdef OFFSETMAPPING + vec2 tcoffsetmap = offsetmap(s_normalmap, tcbase, eyevector); + #define tcbase tcoffsetmap + #endif + + vec4 albedo_f = texture2D(s_diffuse, tcbase); + + #ifdef BUMP + vec3 normal_f = normalize(texture2D(s_normalmap, tcbase).rgb - 0.5); + #else + vec3 normal_f = vec3(0.0, 0.0, 1.0); + #endif + #ifdef ORTHO float colorscale = 1.0; #else float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0); #endif - /* filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows */ #ifdef PCF + /* filter the light by the shadowmap. logically a boolean, but we allow fractions for softer shadows */ colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord); #endif - /* filter the colour by the spotlight. discard anything behind the light so we don't get a mirror image */ - #if defined(SPOT) - if (vtexprojcoord.w < 0.0) - discard; - + #ifdef SPOT + /* filter the colour by the spotlight. discard anything behind the light so we don't get a mirror image */ + if (vtexprojcoord.w < 0.0) discard; vec2 spot = ((vtexprojcoord.st)/vtexprojcoord.w); colorscale*=1.0-(dot(spot,spot)); #endif - #if defined(FLAT) - vec4 bases = vec4(FLAT, FLAT, FLAT, 1.0); - #else - #if r_skipDiffuse == 0 - vec4 bases = texture2D(s_diffuse, tex_c); + if (colorscale > 0) + { + vec3 out_f; + + #ifdef FLAT + albedo_f = vec4(FLAT, FLAT, FLAT, 1.0); #else - vec4 bases = vec4(1.0, 1.0, 1.0, 1.0); - #endif - #endif - - #ifdef BUMP - vec3 normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5) * 2.0; - float refl = 1.0 - texture2D(s_normalmap, tex_c).a; - #endif - - #ifdef NOBUMP - // surface can only support ambient lighting, even for lights that try to avoid it. - vec3 diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y); - #else - vec3 nl = normalize(lightvector); - #ifdef BUMP - vec3 diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(normal_f, nl), 0.0)); - #else - //we still do bumpmapping even without normal_f to ensure colours are always sane. light.exe does it too. - vec3 diff = bases.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); - #endif - #endif - - /* respect the reflectcube surface */ - #ifdef BUMP - vec3 rtc = reflect(-eyevector, normal_f); - rtc = rtc.x * invsurface[0] + rtc.y * invsurface[1] + rtc.z * invsurface[2]; - rtc = (m_model * vec4(rtc.xyz,0.0)).xyz; - diff += textureCube(s_reflectcube, rtc).rgb * refl; - #endif - - /* filter the colour by the cubemap projection */ - #ifdef CUBE - diff *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb; - #endif - - diff.rgb *= bases.a; - - diff *= colorscale * l_lightcolour; - diff.rgb *= vc.a; - - #if defined(UPPERLOWER) - diff.rgb *= (texture2D(s_upper, tex_c * 4.0).rgb + 0.5); + #ifdef VERTEXCOLOURS + albedo_f.rgb *= albedo_f.a; + #endif #endif - gl_FragColor = vec4(fog3additive(diff), vc.a); + #ifdef UPPER + vec4 uc = texture2D(s_upper, tcbase); + albedo_f.rgb += uc.rgb*e_uppercolour*uc.a; + #endif + + #ifdef LOWER + vec4 lc = texture2D(s_lower, tcbase); + albedo_f.rgb += lc.rgb*e_lowercolour*lc.a; + #endif + + #ifdef PBR + float metalness_f =texture2D(s_specular, tcbase).r; + float roughness_f = texture2D(s_specular, tcbase).g; + float ao = texture2D(s_specular, tcbase).b; + + vec3 nl = normalize(lightvector); + out_f = albedo_f.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(normal_f.rgb, nl), 0.0)); + + const vec3 dielectricSpecular = vec3(0.04, 0.04, 0.04); + const vec3 black = vec3(0.0, 0.0, 0.0); + vec3 F0 = mix(dielectricSpecular, albedo_f.rgb, metalness_f); + albedo_f.rgb = mix(albedo_f.rgb * (1.0 - dielectricSpecular.r), black, metalness_f); + + out_f = LightingFuncShlick(normal_f.rgb, normalize(eyevector), nl, roughness_f, albedo_f.rgb, F0); + + vec3 cube_c = reflect(-eyevector, normal_f.rgb); + cube_c = cube_c.x*invsurface[0] + cube_c.y*invsurface[1] + cube_c.z*invsurface[2]; + cube_c = vec4(m_model * vec4(cube_c.xyz,0.0)).xyz; + + out_f.rgb = out_f.rgb + (vec3(metalness_f,metalness_f,metalness_f) * textureCube(s_reflectcube, cube_c).rgb); + #endif + + #ifdef CUBE + /* filter the colour by the cubemap projection */ + out_f *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb; + #endif + + #ifdef PROJECTION + /* 2d projection, not used */ + out_f *= texture2d(s_projectionmap, shadowcoord); + #endif + + #ifdef VERTEXCOLOURS + out_f *= vc.rgb * vc.a; + #endif + + gl_FragColor.rgb = fog3additive(out_f * colorscale * l_lightcolour); + } else { + gl_FragColor.rgb = vec3(0.0); + } } -#endif +#endif diff --git a/base/resources.pk3dir/glsl/skybox.glsl b/base/resources.pk3dir/glsl/skybox.glsl deleted file mode 100644 index 3188c9c2..00000000 --- a/base/resources.pk3dir/glsl/skybox.glsl +++ /dev/null @@ -1,53 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Basic skybox shader with two clouds rendered on top using a dodge filter. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps box:samplerCube=0 cloudA=1 cloudB=2 -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec3 cloudpos; -varying vec3 boxpos; -#ifdef VERTEX_SHADER -void main () -{ - boxpos = v_position.xyz - e_eyepos; - cloudpos = v_position.xyz; - gl_Position = ftetransform(); -} -#endif -#ifdef FRAGMENT_SHADER - - -#define s_cloud1 s_t0 -#define s_cloud2 s_t1 -void main () -{ - vec4 skybox = textureCube( s_box, boxpos ); - vec2 tccoord; - vec3 dir = cloudpos - e_eyepos; - dir.z *= 3.0; - dir.xy /= 0.5 * length( dir ); - tccoord = ( dir.xy + e_time * 0.015 ); - vec4 cloud1_f = texture2D( s_cloudA, tccoord ); - tccoord = ( dir.xy + e_time * 0.02 ); - vec4 cloud2_f = texture2D( s_cloudB, tccoord ); - - vec3 dodged1 = vec3(1.0,1.0,1.0) - (cloud1_f.rgb * vec3(cloud1_f.a, cloud1_f.a, cloud1_f.a)); - vec3 dodged2 = vec3(1.0,1.0,1.0) - (cloud2_f.rgb * vec3(cloud2_f.a, cloud2_f.a, cloud2_f.a)); - - gl_FragColor.rgb = skybox.rgb / dodged1; - gl_FragColor.rgb = gl_FragColor.rgb / dodged2; - gl_FragColor *= e_lmscale; - -#ifdef FOGGED - gl_FragColor.rgb = fog3(gl_FragColor.rgb); -#endif -} -#endif - diff --git a/base/resources.pk3dir/glsl/skybox_hdr.glsl b/base/resources.pk3dir/glsl/skybox_hdr.glsl deleted file mode 100644 index 95147d8e..00000000 --- a/base/resources.pk3dir/glsl/skybox_hdr.glsl +++ /dev/null @@ -1,63 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Basic skybox shader with two clouds rendered on top using a dodge filter. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps hdr_40:samplerCube=0 hdr_250:samplerCube=1 hdr_1600:samplerCube=2 cloudA=3 cloudB=4 -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec3 cloudpos; -varying vec3 boxpos; - -#ifdef VERTEX_SHADER -void main () -{ - boxpos = v_position.xyz - e_eyepos; - cloudpos = v_position.xyz; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - float hdr_scale; - vec3 sky_out; - vec3 skybox_40 = textureCube( s_hdr_40, boxpos ).rgb; - vec3 skybox_250 = textureCube( s_hdr_250, boxpos ).rgb; - vec3 skybox_1600 = textureCube( s_hdr_1600, boxpos ).rgb; - hdr_scale = (e_lmscale.r + e_lmscale.g + e_lmscale.b) / 3.0; - - if (hdr_scale > 1.0) { - sky_out = mix(skybox_250, skybox_40, hdr_scale - 1.0); - } else { - sky_out = mix(skybox_1600, skybox_250, hdr_scale); - } - - /* the cloud bits */ - vec2 tccoord; - vec3 dir = cloudpos - e_eyepos; - dir.z *= 3.0; - dir.xy /= 0.5 * length( dir ); - tccoord = ( dir.xy + e_time * 0.015 ); - vec4 cloud1_f = texture2D( s_cloudA, tccoord ); - tccoord = ( dir.xy + e_time * 0.02 ); - vec4 cloud2_f = texture2D( s_cloudB, tccoord ); - - vec3 dodged1 = vec3(1.0,1.0,1.0) - (cloud1_f.rgb * vec3(cloud1_f.a, cloud1_f.a, cloud1_f.a)); - vec3 dodged2 = vec3(1.0,1.0,1.0) - (cloud2_f.rgb * vec3(cloud2_f.a, cloud2_f.a, cloud2_f.a)); - - gl_FragColor.rgb = sky_out / dodged1; - gl_FragColor.rgb = gl_FragColor.rgb / dodged2; - -#ifdef FOGGED - gl_FragColor.rgb = fog3(gl_FragColor.rgb); -#endif -} -#endif - diff --git a/base/resources.pk3dir/glsl/skybox_parallax.glsl b/base/resources.pk3dir/glsl/skybox_parallax.glsl deleted file mode 100644 index 6130947d..00000000 --- a/base/resources.pk3dir/glsl/skybox_parallax.glsl +++ /dev/null @@ -1,54 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// A skybox cube with two cloud layers, which get occluded by a blended -// second skybox cube. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps cloudA=0 cloudB=1 box:samplerCube=2 mountains:samplerCube=3 -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec3 cloudpos; -varying vec3 boxpos; -#ifdef VERTEX_SHADER -void main () -{ - boxpos = v_position.xyz - e_eyepos; - cloudpos = v_position.xyz; - gl_Position = ftetransform(); -} -#endif -#ifdef FRAGMENT_SHADER - -void main () -{ - vec4 mountains = textureCube( s_mountains, boxpos ); - vec4 box = textureCube( s_box, boxpos ); - vec2 tccoord; - vec3 dir = cloudpos - e_eyepos; - dir.z *= 3.0; - dir.xy /= 0.5 * length( dir ); - tccoord = ( dir.xy + e_time * 0.015 ); - vec4 cloud1_f = texture2D( s_cloudA, tccoord ); - tccoord = ( dir.xy + e_time * 0.02 ); - vec4 cloud2_f = texture2D( s_cloudA, tccoord ); - - gl_FragColor.rgb = mix( box.rgb, cloud1_f.rgb, cloud1_f.a ); - gl_FragColor = vec4( mix( gl_FragColor.rgb, cloud2_f.rgb, cloud2_f.a ), 1.0 ); - - if (mountains.a > 0.9) { - gl_FragColor.rgb = mix( gl_FragColor.rgb, mountains.rgb, mountains.a); - } - - gl_FragColor *= e_lmscale; - -#ifdef FOGGED - gl_FragColor.rgb = fog3(gl_FragColor.rgb); -#endif -} -#endif - diff --git a/base/resources.pk3dir/glsl/skybox_valley.glsl b/base/resources.pk3dir/glsl/skybox_valley.glsl deleted file mode 100644 index 2b1d3475..00000000 --- a/base/resources.pk3dir/glsl/skybox_valley.glsl +++ /dev/null @@ -1,53 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Terrain shader for tw_valley's skyroom. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps box:samplerCube=0 cloudA=1 cloudB=2 -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec3 cloudpos; -varying vec3 boxpos; -#ifdef VERTEX_SHADER -void main () -{ - boxpos = v_position.xyz - e_eyepos; - cloudpos = v_position.xyz; - gl_Position = ftetransform(); -} -#endif -#ifdef FRAGMENT_SHADER - - -#define s_cloud1 s_t0 -#define s_cloud2 s_t1 -void main () -{ - vec4 skybox = textureCube( s_box, boxpos ); - vec2 tccoord; - vec3 dir = cloudpos - e_eyepos; - dir.z *= 4.0; - dir.xy /= 0.5 * length( dir ); - tccoord = ( dir.xy + e_time * 0.015 ) * 0.75; - vec4 cloud1_f = texture2D( s_cloudA, tccoord ); - tccoord = ( dir.xy + e_time * 0.02 ) * 0.75; - vec4 cloud2_f = texture2D( s_cloudB, tccoord ); - - vec3 dodged1 = vec3(1.0,1.0,1.0) - (cloud1_f.rgb * vec3(cloud1_f.a, cloud1_f.a, cloud1_f.a)); - vec3 dodged2 = vec3(1.0,1.0,1.0) - (cloud2_f.rgb * vec3(cloud2_f.a, cloud2_f.a, cloud2_f.a)); - - gl_FragColor.rgb = skybox.rgb / dodged1; - gl_FragColor.rgb = gl_FragColor.rgb / dodged2; - gl_FragColor *= e_lmscale; - -#ifdef FOGGED - gl_FragColor.rgb = fog3(gl_FragColor.rgb); -#endif -} -#endif - diff --git a/base/resources.pk3dir/glsl/sprite.glsl b/base/resources.pk3dir/glsl/sprite.glsl deleted file mode 100644 index c0f6f10d..00000000 --- a/base/resources.pk3dir/glsl/sprite.glsl +++ /dev/null @@ -1,33 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Trisoup whose diffusemap multiplies against the glColor values. -//============================================================================== - -!!ver 110 -!!samps diffuse - -varying vec2 tex_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; - void main () - { - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main () - { - vec4 diffuse_f = vex_color; - diffuse_f.rgb *= diffuse_f.a; - diffuse_f *= texture2D(s_diffuse, tex_c); - gl_FragColor = diffuse_f; - } -#endif diff --git a/base/resources.pk3dir/glsl/sprite_fixed.glsl b/base/resources.pk3dir/glsl/sprite_fixed.glsl deleted file mode 100644 index c0f6f10d..00000000 --- a/base/resources.pk3dir/glsl/sprite_fixed.glsl +++ /dev/null @@ -1,33 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Trisoup whose diffusemap multiplies against the glColor values. -//============================================================================== - -!!ver 110 -!!samps diffuse - -varying vec2 tex_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER -attribute vec2 v_texcoord; -attribute vec4 v_colour; - void main () - { - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main () - { - vec4 diffuse_f = vex_color; - diffuse_f.rgb *= diffuse_f.a; - diffuse_f *= texture2D(s_diffuse, tex_c); - gl_FragColor = diffuse_f; - } -#endif diff --git a/base/resources.pk3dir/glsl/sprite_vscroll.glsl b/base/resources.pk3dir/glsl/sprite_vscroll.glsl deleted file mode 100644 index d166f439..00000000 --- a/base/resources.pk3dir/glsl/sprite_vscroll.glsl +++ /dev/null @@ -1,34 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Trisoup whose diffusemap multiplies against the glColor values. -//============================================================================== - -!!ver 110 -!!samps diffuse - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER - void main () - { - tex_c = v_texcoord; - tex_c.y -= e_time; - vex_color = v_colour; - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - void main () - { - vec4 diffuse_f = vex_color; - diffuse_f.rgb *= diffuse_f.a; - diffuse_f *= texture2D(s_diffuse, tex_c); - gl_FragColor = diffuse_f; - } -#endif diff --git a/base/resources.pk3dir/glsl/terrain.glsl b/base/resources.pk3dir/glsl/terrain.glsl deleted file mode 100644 index c75145c2..00000000 --- a/base/resources.pk3dir/glsl/terrain.glsl +++ /dev/null @@ -1,167 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Default terrain shader that blends between two surfaces and creates a -// realistic transition using the diffusemaps' monochrome channel for masking. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!permu UPPERLOWER -!!samps 6 - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!samps =UPPERLOWER upper - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipNormal -!!cvardf r_skipLightmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main ( void ) - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return lightmaps; -#endif - } -#endif - - void main ( void ) - { - vec4 diff1_f = texture2D( s_t0, tex_c); - vec4 diff2_f = texture2D( s_t1, tex_c); - float alpha = 1.0; - float bw = 1.0 - (diff2_f.r + diff2_f.g + diff2_f.b) / 3.0; - - #if r_skipNormal==0 - vec3 normal1_f = normalize(texture2D(s_t2, tex_c).rgb - 0.5); - vec3 normal2_f = normalize(texture2D(s_t3, tex_c).rgb - 0.5); - #endif - - if (vex_color.a < 1.0) { - if (bw > vex_color.a) { - alpha = 0.0; - } - } - - /* light */ - #if r_skipNormal==0 - diff1_f.rgb *= lightmap_fragment(normal1_f); - diff2_f.rgb *= lightmap_fragment(normal2_f); - #else - diff1_f.rgb *= lightmap_fragment(); - diff2_f.rgb *= lightmap_fragment(); - #endif - - vec3 output_f = mix(diff1_f.rgb, diff2_f.rgb, alpha); - - #ifdef FAKESHADOWS - output_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - #if defined(UPPERLOWER) - output_f.rgb *= (texture2D(s_upper, tex_c * 4.0).rgb + 0.5); - #endif - - gl_FragColor = fog4( vec4( output_f.rgb, 1.0 ) ); - } -#endif diff --git a/base/resources.pk3dir/glsl/terrain_alpha.glsl b/base/resources.pk3dir/glsl/terrain_alpha.glsl deleted file mode 100644 index 9448d78a..00000000 --- a/base/resources.pk3dir/glsl/terrain_alpha.glsl +++ /dev/null @@ -1,169 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Blending terrain and masking its edges for a smooth transition into alpha. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!permu UPPERLOWER -!!samps diffuse normalmap - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!samps =UPPERLOWER upper - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipLightmap -!!cvardf r_skipNormal - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main ( void ) - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return lightmaps; -#endif - } -#endif - - void main ( void ) - { - #if r_skipDiffuse==0 - vec3 diffuse_f = texture2D(s_diffuse, tex_c).rgb; - #else - vec3 diffuse_f = vec3(1.0,1.0,1.0); - #endif - - float bw = 1.0 - (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - vec4 vcol = vex_color; - - #if r_skipNormal==0 - vec3 normal_f; - normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #endif - - if (vcol.a < 1.0) { - if (bw > vcol.a) { - discard; - } - } - - #if r_skipNormal==0 - diffuse_f.rgb *= lightmap_fragment(normal_f); - #else - diffuse_f.rgb *= lightmap_fragment(); - #endif - - #ifdef FAKESHADOWS - diffuse_f *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - - #if defined(UPPERLOWER) - diffuse_f.rgb *= (texture2D(s_upper, tex_c * 4.0).rgb + 0.5); - #endif - - gl_FragColor = vec4(fog3(diffuse_f), 1.0); - } -#endif - diff --git a/base/resources.pk3dir/glsl/terrain_alpha_alt.glsl b/base/resources.pk3dir/glsl/terrain_alpha_alt.glsl deleted file mode 100644 index 9d79d910..00000000 --- a/base/resources.pk3dir/glsl/terrain_alpha_alt.glsl +++ /dev/null @@ -1,173 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Alternative way of blending/masking terrain between to diffuse textures. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!permu UPPERLOWER -!!samps diffuse normalmap - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!samps =UPPERLOWER upper - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipLightmap -!!cvardf r_skipNormal - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main ( void ) - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return lightmaps; -#endif - } -#endif - - void main ( void ) - { - #if r_skipDiffuse==0 - vec3 diffuse_f = texture2D(s_diffuse, tex_c).rgb; - #else - vec3 diffuse_f = vec3(1.0,1.0,1.0); - #endif - - float bw = (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - vec4 vcol = vex_color; - float alpha = 1.0; - - #if r_skipNormal==0 - vec3 normal_f; - normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5) * 2.0; - #endif - - if (vcol.a < 1.0) { - if (bw > vcol.a) { - discard; - } - } - - if (bw > (vcol.a * 0.25)) - alpha = vcol.a; - - #if r_skipNormal==0 - diffuse_f.rgb *= lightmap_fragment(normal_f); - #else - diffuse_f.rgb *= lightmap_fragment(); - #endif - - #ifdef FAKESHADOWS - diffuse_f *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - - #if defined(UPPERLOWER) - diffuse_f.rgb *= texture2D(s_upper, tex_c * 4.0).rgb; - #endif - - gl_FragColor = vec4(fog3(diffuse_f), alpha); - } -#endif - diff --git a/base/resources.pk3dir/glsl/terrain_mask.glsl b/base/resources.pk3dir/glsl/terrain_mask.glsl deleted file mode 100644 index 654a267a..00000000 --- a/base/resources.pk3dir/glsl/terrain_mask.glsl +++ /dev/null @@ -1,172 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Blending terrain and masking its edges for a smooth transition into alpha. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!permu UPPERLOWER -!!samps diffuse normalmap - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!samps =UPPERLOWER upper - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!cvardf r_fullbright -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipLightmap -!!cvardf r_skipNormal - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main ( void ) - { - lightmapped_init(); - tex_c = v_texcoord; - vex_color = v_colour; - - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return (r_fullbright == 1) ? vec3(1.0, 1.0, 1.0) : lightmaps; -#endif - } -#endif - - void main ( void ) - { - #if r_skipDiffuse==0 - vec3 diffuse_f = texture2D(s_diffuse, tex_c).rgb; - #else - vec3 diffuse_f = vec3(1.0,1.0,1.0); - #endif - - float bw = 1.0 - (diffuse_f.r + diffuse_f.g + diffuse_f.b) / 3.0; - vec4 vcol = vex_color; - - #if r_skipNormal==0 - vec3 normal_f; - normal_f = normalize(texture2D(s_normalmap, tex_c).rgb - 0.5); - #endif - - if (vcol.a < 1.0) { - // contrast enhancement - #ifdef BWMASK - bw *= ((bw * 2.0) * BWMASK); - #endif - if (bw > vcol.a) { - discard; - } - } - - #if r_skipNormal==0 - diffuse_f.rgb *= lightmap_fragment(normal_f); - #else - diffuse_f.rgb *= lightmap_fragment(); - #endif - - #ifdef FAKESHADOWS - diffuse_f *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - #if defined(UPPERLOWER) - diffuse_f.rgb *= (texture2D(s_upper, tex_c * 4.0).rgb + 0.5); - #endif - - gl_FragColor = vec4(fog3(diffuse_f), 1.0); - } -#endif diff --git a/base/resources.pk3dir/glsl/terrain_skybox.glsl b/base/resources.pk3dir/glsl/terrain_skybox.glsl deleted file mode 100644 index ddfb1151..00000000 --- a/base/resources.pk3dir/glsl/terrain_skybox.glsl +++ /dev/null @@ -1,120 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Terrain shader used for skyroom surfaces. -//============================================================================== - -!!ver 110 -!!permu FOG -!!permu BUMP -!!permu DELUXE -!!samps 4 diffuse - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 -!!cvardf r_skipLightmap - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main () - { - tex_c = v_texcoord; - lm_c = v_lmcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - } -#endif - -#ifdef FRAGMENT_SHADER - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return lightmaps; -#endif - } -#endif - - void main () - { - vec4 diff1_f = texture2D( s_t0, tex_c ); - vec4 diff2_f = texture2D( s_t1, tex_c ); - vec3 output_f = mix( diff1_f.rgb, diff2_f.rgb, vex_color.a ) * lightmap_fragment(normal_f); - gl_FragColor = fog4( vec4( output_f.rgb, 1.0 ) ); - } -#endif diff --git a/base/resources.pk3dir/glsl/terrain_valley.glsl b/base/resources.pk3dir/glsl/terrain_valley.glsl deleted file mode 100644 index a7115abb..00000000 --- a/base/resources.pk3dir/glsl/terrain_valley.glsl +++ /dev/null @@ -1,155 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Terrain shader exclusive to tw_valley. One of the few surfaces that do not -// draw a normalmap as it's too expensive. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps 6 - -!!samps lightmap -!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 -!!samps =DELUXE deluxemap -!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipNormal -!!cvardf r_skipLightmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec2 detail_c; -varying vec4 vex_color; - -varying vec2 lm0; -#ifdef LIGHTSTYLED -varying vec2 lm1, lm2, lm3; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - void lightmapped_init(void) - { - lm0 = v_lmcoord; - #ifdef LIGHTSTYLED - lm1 = v_lmcoord2; - lm2 = v_lmcoord3; - lm3 = v_lmcoord4; - #endif - } - - void main ( void ) - { - lightmapped_init(); - tex_c = v_texcoord * 2.5; - detail_c = tex_c * 7.5; - vex_color = v_colour; - gl_Position = ftetransform(); - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#if r_skipLightmap==0 - #ifdef LIGHTSTYLED - #define LIGHTMAP0 texture2D(s_lightmap0, lm0).rgb - #define LIGHTMAP1 texture2D(s_lightmap1, lm1).rgb - #define LIGHTMAP2 texture2D(s_lightmap2, lm2).rgb - #define LIGHTMAP3 texture2D(s_lightmap3, lm3).rgb - #else - #define LIGHTMAP texture2D(s_lightmap, lm0).rgb - #endif -#else - #ifdef LIGHTSTYLED - #define LIGHTMAP0 vec3(0.5,0.5,0.5) - #define LIGHTMAP1 vec3(0.5,0.5,0.5) - #define LIGHTMAP2 vec3(0.5,0.5,0.5) - #define LIGHTMAP3 vec3(0.5,0.5,0.5) - #else - #define LIGHTMAP vec3(0.5,0.5,0.5) - #endif -#endif - - vec3 lightmap_fragment() - { - vec3 lightmaps; - -#ifdef LIGHTSTYLED - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb; - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb; - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb; - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb; -#else - lightmaps = LIGHTMAP * e_lmscale.rgb; -#endif - return lightmaps; - } - -#if r_skipNormal==0 - vec3 lightmap_fragment(vec3 normal_f) - { -#ifndef DELUXE - return lightmap_fragment(); -#else - vec3 lightmaps; - - #if defined(LIGHTSTYLED) - lightmaps = LIGHTMAP0 * e_lmscale[0].rgb * dot(normal_f, (texture2D(s_deluxemap0, lm0).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP1 * e_lmscale[1].rgb * dot(normal_f, (texture2D(s_deluxemap1, lm1).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP2 * e_lmscale[2].rgb * dot(normal_f, (texture2D(s_deluxemap2, lm2).rgb - 0.5) * 2.0); - lightmaps += LIGHTMAP3 * e_lmscale[3].rgb * dot(normal_f, (texture2D(s_deluxemap3, lm3).rgb - 0.5) * 2.0); - #else - lightmaps = LIGHTMAP * e_lmscale.rgb * dot(normal_f, (texture2D(s_deluxemap, lm0).rgb - 0.5) * 2.0); - #endif - - return lightmaps; -#endif - } -#endif - - void main ( void ) - { - vec4 diff1_f = texture2D(s_t0, tex_c); - vec4 diff2_f = texture2D(s_t1, tex_c); - vec3 norm1_f = normalize(texture2D(s_t4, tex_c).rgb - 0.5); - vec3 norm2_f = normalize(texture2D(s_t5, tex_c).rgb - 0.5); - - vec3 d1_f = texture2D(s_t2, detail_c).rgb; - vec3 d2_f = texture2D(s_t3, detail_c).rgb; - diff1_f.rgb *= d1_f; - diff2_f.rgb *= d2_f; - - if (float(r_skipNormal) == 1.0) { - diff1_f.rgb *= lightmap_fragment(); - diff2_f.rgb *= lightmap_fragment(); - } else { - diff1_f.rgb *= lightmap_fragment(norm1_f); - diff2_f.rgb *= lightmap_fragment(norm2_f); - } - - vec3 output_f = mix( diff1_f.rgb, diff2_f.rgb, vex_color.a ); - - #ifdef FAKESHADOWS - output_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - gl_FragColor = fog4( vec4( output_f.rgb, 1.0 ) ); - } -#endif diff --git a/base/resources.pk3dir/glsl/unlit.glsl b/base/resources.pk3dir/glsl/unlit.glsl deleted file mode 100644 index 266c38ba..00000000 --- a/base/resources.pk3dir/glsl/unlit.glsl +++ /dev/null @@ -1,38 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Unlit surface. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffuse - -#include "sys/defs.h" -#include "sys/fog.h" - -varying vec2 tex_c; - -#ifdef VERTEX_SHADER -void main () -{ - tex_c = v_texcoord; - gl_Position = ftetransform(); -} -#endif - -#ifdef FRAGMENT_SHADER -void main () -{ - vec4 d_f = texture2D( s_diffuse, tex_c ); - -#ifdef MASK - // alpha-testing happens here - if (d_f.a < MASK) - discard; -#endif - - gl_FragColor = fog4( d_f ); -} -#endif diff --git a/base/resources.pk3dir/glsl/vertexlit.glsl b/base/resources.pk3dir/glsl/vertexlit.glsl deleted file mode 100644 index 1b84679e..00000000 --- a/base/resources.pk3dir/glsl/vertexlit.glsl +++ /dev/null @@ -1,145 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightgrid-lit surface, normalmap's alpha contains environment cube reflec- -// tivity. -//============================================================================== - -!!ver 110 -!!permu FRAMEBLEND -!!permu FULLBRIGHT -!!permu FOG -!!permu BUMP -!!permu SKELETAL -!!samps diffuse -!!samps =BUMP normalmap reflectcube -!!samps =FULLBRIGHT fullbright - -!!cvardf r_skipDiffuse -!!cvardf r_skipFullbright -!!cvardf r_skipNormal -!!cvardf r_skipEnvmap -!!cvardf r_skipLightmap -!!cvardf r_showEnvCubemap - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!cvardf r_fullbright -!!cvardf r_lambertscale -!!samps =FAKESHADOWS shadowmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 norm; - -#ifdef BUMP -varying vec3 eyevector; -varying mat3 invsurface; -#endif - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - #include "sys/skeletal.h" - - void main () - { - vec3 n, s, t, w; - gl_Position = skeletaltransform_wnst(w,n,s,t); - norm = n; - n = normalize(n); - s = normalize(s); - t = normalize(t); - tex_c = v_texcoord; - - #ifdef BUMP - /* normalmap */ - invsurface = mat3(s, t, n); - - /* reflect */ - vec3 eyeminusvertex = e_eyepos - w.xyz; - eyevector.x = dot(eyeminusvertex, s.xyz); - eyevector.y = dot(eyeminusvertex, t.xyz); - eyevector.z = dot(eyeminusvertex, n.xyz); - #endif - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#ifdef HALFLAMBERT - float lambert(vec3 normal, vec3 dir) - { - return (lambert(normal, dir) * 0.5) + 0.5; - } -#else - float lambert(vec3 normal, vec3 dir) - { - return max(dot(normal, dir), 0.0); - } -#endif - - void main (void) - { - vec4 diff_f = vec4(1.0, 1.0, 1.0, 1.0); - vec3 light = vec3(0.0, 0.0, 0.0); - - #if r_skipDiffuse == 0 - diff_f = texture2D(s_diffuse, tex_c); - #endif - - // bump goes here - #if r_skipNormal==0 || defined(BUMP) - vec3 normal_f = (texture2D(s_normalmap, tex_c).rgb - 0.5) * 2.0; - #else - vec3 normal_f = vec3(0.0, 0.0, 1.0); - #endif - - #ifdef MASK - if (diff_f.a < MASK) { - discard; - } - #endif - - /* directional light */ - light += (e_light_mul * lambert(norm, e_light_dir)) * 2.0; - light += (e_light_ambient * lambert(norm, reflect(norm, e_light_dir))) * 0.5; - light += (e_light_mul * dot(normal_f, e_light_dir)); - - #ifdef FAKESHADOWS - diff_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - diff_f.rgb *= light; - - #if defined(BUMP) && r_skipEnvmap==0 - vec3 cube_c; - float refl = 1.0 - texture2D(s_normalmap, tex_c).a; - - cube_c = reflect(normalize(eyevector), norm); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - - #if r_showEnvCubemap == 0 - diff_f.rgb += textureCube(s_reflectcube, cube_c).rgb * refl; - #else - diff_f.rgb = textureCube(s_reflectcube, cube_c).rgb; - #endif - #endif - - #if defined(FULLBRIGHT) && r_skipFullbright==0 - diff_f.rgb += texture2D(s_fullbright, tex_c).rgb; - #endif - - gl_FragColor = fog4(diff_f * e_colourident) * e_lmscale; - } -#endif diff --git a/base/resources.pk3dir/glsl/vertexlit_specular.glsl b/base/resources.pk3dir/glsl/vertexlit_specular.glsl deleted file mode 100644 index 29ddfd4e..00000000 --- a/base/resources.pk3dir/glsl/vertexlit_specular.glsl +++ /dev/null @@ -1,148 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Lightgrid-lit surface, normalmap's alpha contains specularity information. -//============================================================================== - -!!ver 110 -!!permu FRAMEBLEND -!!permu FOG -!!permu SKELETAL -!!cvarf gl_specular -!!samps diffuse fullbright normalmap - -!!permu FAKESHADOWS -!!cvardf r_glsl_pcf -!!samps =FAKESHADOWS shadowmap - -!!cvardf r_skipDiffuse -!!cvardf r_skipSpecular -!!cvardf r_skipNormal - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec3 eyevector; -varying vec3 norm; -varying mat3 invsurface; - -#ifdef FAKESHADOWS - varying vec4 vtexprojcoord; -#endif - -#ifdef VERTEX_SHADER - #include "sys/skeletal.h" - -#ifdef CHROME - /* Rotate Light Vector */ - vec3 rlv(vec3 axis, vec3 origin, vec3 lightpoint) - { - vec3 offs; - vec3 result; - offs[0] = lightpoint[0] - origin[0]; - offs[1] = lightpoint[1] - origin[1]; - offs[2] = lightpoint[2] - origin[2]; - result[0] = dot(offs[0], axis[0]); - result[1] = dot(offs[1], axis[1]); - result[2] = dot(offs[2], axis[2]); - return result; - } -#endif - - void main () - { - vec3 n, s, t, w; - gl_Position = skeletaltransform_wnst(w,n,s,t); - norm = n; - n = normalize(n); - s = normalize(s); - t = normalize(t); - - #ifdef CHROME - vec3 rorg = rlv(vec3(0,0,0), w, e_eyepos); - vec3 viewc = normalize(e_eyepos - w); - float d = dot(n, viewc); - vec3 reflected; - reflected.x = n.x * 2.0 * d - viewc.x; - reflected.y = n.y * 2.0 * d - viewc.y; - reflected.z = n.z * 2.0 * d - viewc.z; - tex_c.x = 0.5 + reflected.y * 0.5; - tex_c.y = 0.5 - reflected.z * 0.5; - #else - tex_c = v_texcoord; - #endif - - /* normalmap */ - invsurface = mat3(s, t, n); - - /* reflect */ - eyevector = e_eyepos - w.xyz; - - #ifdef FAKESHADOWS - vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0)); - #endif - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - #include "sys/pcf.h" - -#ifdef HALFLAMBERT - float lambert(vec3 normal, vec3 dir) - { - return (lambert(normal, dir) * 0.5) + 0.5; - } -#else - float lambert(vec3 normal, vec3 dir) - { - return max(dot(normal, dir), 0.0); - } -#endif - - void main () - { - vec4 fb_f = texture2D(s_fullbright, tex_c); - vec3 light; - - #if r_skipDiffuse==0 - vec4 diffuse_f = texture2D(s_diffuse, tex_c); - #else - vec4 diffuse_f = vec4(1.0,1.0,1.0,1.0); - #endif - - #if r_skipNormal==0 - vec3 normal_f = (texture2D(s_normalmap, tex_c).rgb - 0.5) * 2.0; - float gloss = texture2D(s_normalmap, tex_c).a; - #else - #define normal_f vec3(0.0,0.0,1.0) - float gloss = texture2D(s_normalmap, tex_c).a; - #endif - - if (diffuse_f.a < 0.5) { - discard; - } - - light = (e_light_mul * lambert(norm, e_light_dir)) * 2.0; /* directional light */ - light += (e_light_ambient * lambert(norm, reflect(norm, e_light_dir))) * 0.5; /* reverse ambient */ - light *= 2.0; - - #if r_skipSpecular==0 - vec3 halfdir = normalize(normalize(eyevector) + e_light_dir); - vec3 bumps = normalize(invsurface * (normal_f)); - float spec = pow(max(dot(halfdir, bumps), 0.0), FTE_SPECULAR_EXPONENT); - spec *= 5.0 * (1.0 - gloss); - diffuse_f.rgb += spec; - #endif - - diffuse_f.rgb *= light; - diffuse_f.rgb += fb_f.rgb; - - #ifdef FAKESHADOWS - diffuse_f.rgb *= ShadowmapFilter(s_shadowmap, vtexprojcoord); - #endif - - gl_FragColor = fog4( diffuse_f * e_colourident ) * e_lmscale; - } -#endif diff --git a/base/resources.pk3dir/glsl/vertexmap.glsl b/base/resources.pk3dir/glsl/vertexmap.glsl deleted file mode 100644 index 3f9be75b..00000000 --- a/base/resources.pk3dir/glsl/vertexmap.glsl +++ /dev/null @@ -1,36 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Trisoup whose diffusemap multiplies against the glColor values. -//============================================================================== - -!!ver 110 -!!permu FRAMEBLEND -!!permu FOG -!!samps diffuse - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec4 vex_color; - -#ifdef VERTEX_SHADER - void main () - { - tex_c = v_texcoord; - vex_color = v_colour; - gl_Position = ftetransform(); - } -#endif - - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main () - { - vec4 diffuse_f = texture2D( s_diffuse, tex_c ); - diffuse_f.rgb *= vex_color.rgb; - gl_FragColor = fog4( diffuse_f ); - } -#endif diff --git a/base/resources.pk3dir/glsl/water.glsl b/base/resources.pk3dir/glsl/water.glsl deleted file mode 100644 index 4b9e2a24..00000000 --- a/base/resources.pk3dir/glsl/water.glsl +++ /dev/null @@ -1,70 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Basic water shader, No diffuse texture, just pure normalmap + refraction -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps 2 normalmap reflectcube - -#include "sys/defs.h" - -varying mat3 invsurface; -varying vec4 tf_c; -varying vec3 eyeminusvertex; -varying vec3 eyevector; -varying vec2 shift1; -varying vec2 shift2; - -#ifdef VERTEX_SHADER - void main () - { - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot( eyeminusvertex, v_svector.xyz ); - eyevector.y = dot( eyeminusvertex, v_tvector.xyz ); - eyevector.z = dot( eyeminusvertex, v_normal.xyz ); - tf_c = ftetransform(); - tf_c.z += 0.1; /* hack to get rid of refraction artifacts */ - shift1 = v_texcoord + vec2( e_time * 0.1, 0.0 ); - shift2 = v_texcoord - vec2( 0, e_time * -0.01 ); - gl_Position = tf_c; - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main ( void ) - { - float fres; - vec2 refl_c; - vec3 refl_f; - vec3 refr_f; - vec3 norm_f; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - - // Use the normalmap to shift the refraction - norm_f = ( texture2D( s_normalmap, shift1 ).xyz); - norm_f += ( texture2D( s_normalmap, shift2 ).xyz); - norm_f -= 1.0 - ( 4.0 / 256.0 ); - norm_f = normalize( norm_f ) * 0.05; - - // Reflection/View coordinates - refl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5; - refl_c.t -= 1.5 * invsurface[2].z / 1080.0; - - vec3 cube_c = reflect( normalize( -eyevector ), texture2D( s_normalmap, shift2).rgb * 0.35 ); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = ( m_model * vec4( cube_c.xyz, 0.0 ) ).xyz; - refl_f = textureCube( s_reflectcube, cube_c ).rgb; - refr_f = texture2D( s_t1, refl_c + ( norm_f.st * 0.1 ) ).rgb; - fres = pow( 1.0 - abs( dot( norm_f, normalize( eyeminusvertex ) ) ), 5.0 ); - out_f.rgb = mix( refr_f, refl_f, fres * 0.25 ); - - gl_FragColor = fog4( out_f ); - } -#endif diff --git a/base/resources.pk3dir/glsl/water_dirty.glsl b/base/resources.pk3dir/glsl/water_dirty.glsl deleted file mode 100644 index 77d7888f..00000000 --- a/base/resources.pk3dir/glsl/water_dirty.glsl +++ /dev/null @@ -1,59 +0,0 @@ -//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Dirty water shader (diffuse and normalmap only) where a refraction-ish effect -// is applied onto the diffusemap itself. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps diffuse normalmap - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec2 lm_c; -varying vec3 invsurface; -varying vec4 tf_c; -varying vec3 eyeminusvertex; -varying vec2 wat1_c; -varying vec2 wat2_c; - -#ifdef VERTEX_SHADER - void main(void) - { - invsurface = v_normal; - eyeminusvertex = e_eyepos - v_position.xyz; - - tf_c = ftetransform(); - tex_c = v_texcoord; - gl_Position = tf_c; - - wat1_c = tex_c + vec2(e_time * 0.05, 0.0); - wat2_c = tex_c - vec2(0, e_time * 0.05); - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main(void) - { - vec3 norm_f; - vec4 out_f = vec4(1.0, 1.0, 1.0, 1.0); - vec2 wat3_c; - - // Use the normalmap to shift the refraction - norm_f = (texture2D(s_normalmap, wat1_c).xyz); - norm_f += (texture2D(s_normalmap, wat2_c).xyz); - norm_f -= 1.0 - (4.0 / 256.0); - norm_f = normalize(norm_f); - - wat3_c = tex_c + (norm_f.st * 0.025) + vec2(sin(e_time * 0.1), 0); - - // Load reflection and refraction based on our new coords - out_f.rgb = texture2D(s_diffuse, wat3_c).rgb; - - gl_FragColor = fog4(out_f); - } -#endif diff --git a/base/resources.pk3dir/glsl/water_high.glsl b/base/resources.pk3dir/glsl/water_high.glsl deleted file mode 100644 index a847367c..00000000 --- a/base/resources.pk3dir/glsl/water_high.glsl +++ /dev/null @@ -1,54 +0,0 @@ -!!ver 110 -!!permu FOG -!!samps reflection=0 refraction=1 normalmap=2 - -#include "sys/defs.h" - -varying vec2 tex_c; -varying mat3 invsurface; -varying vec4 tf_c; -varying vec3 eyeminusvertex; - -#ifdef VERTEX_SHADER - void main () - { - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - eyeminusvertex = e_eyepos - v_position.xyz; - tf_c = ftetransform(); - tf_c.z += 0.1; /* hack to get rid of refraction artifacts */ - tex_c = v_texcoord; - gl_Position = tf_c; - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main ( void ) - { - float fres; - vec2 refl_c; - vec3 refl_f; - vec3 refr_f; - vec3 norm_f; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - - norm_f = ( texture2D( s_normalmap, tex_c + vec2( e_time * 0.01, 0.0 ) ).xyz); - norm_f += ( texture2D( s_normalmap, tex_c - vec2( 0, e_time * 0.01 ) ).xyz); - norm_f -= 1.0 - ( 4.0 / 256.0 ); - norm_f = normalize( norm_f ); - - // Reflection/View coordinates - refl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5; - refl_c.t -= 1.5 * invsurface[2].z / 1080.0; - - refl_f = texture2D( s_reflection, refl_c ).rgb; - refr_f = texture2D( s_refraction, refl_c + ( norm_f.st) ).rgb; - - fres = pow( 1.0 - abs( dot( norm_f, normalize( eyeminusvertex ) ) ), 5.0 ); - out_f.rgb = mix( refr_f, refl_f, fres ); - - gl_FragColor = fog4( out_f ); - } -#endif diff --git a/base/resources.pk3dir/glsl/water_sky.glsl b/base/resources.pk3dir/glsl/water_sky.glsl deleted file mode 100644 index cd77f6b3..00000000 --- a/base/resources.pk3dir/glsl/water_sky.glsl +++ /dev/null @@ -1,80 +0,0 @@ -//======= Copyright (c) 2015-2022 Vera Visions LLC. All rights reserved. ======= -// -// Purpose: -// -// Water shader that distorts the reflection based on a supplied normalmap -// and blends it on top of a skybox (cube) image. -//============================================================================== - -!!ver 110 -!!permu FOG -!!samps reflect=0 norm=1 skycube:samplerCube=2 - -#include "sys/defs.h" - -varying vec2 tex_c; -varying vec2 lm_c; -varying mat3 invsurface; -varying vec4 tf_c; -varying vec3 eyeminusvertex; - -varying vec3 eyevector; -varying vec2 shift1; -varying vec2 shift2; - -#ifdef VERTEX_SHADER - void main () - { - invsurface[0] = v_svector; - invsurface[1] = v_tvector; - invsurface[2] = v_normal; - eyeminusvertex = e_eyepos - v_position.xyz; - eyevector.x = dot(eyeminusvertex, v_svector.xyz); - eyevector.y = dot(eyeminusvertex, v_tvector.xyz); - eyevector.z = dot(eyeminusvertex, v_normal.xyz); - - tf_c = ftetransform(); - tf_c.z += 0.1; - shift1 = v_texcoord + vec2( e_time * 0.025, 0.0 ); - shift2 = v_texcoord - vec2( 0, e_time * -0.025 ); - tex_c = v_texcoord; - gl_Position = tf_c; - } -#endif - -#ifdef FRAGMENT_SHADER - #include "sys/fog.h" - void main ( void ) - { - float fres; - vec2 refl_c; - vec3 refl_f; - vec3 refr_f; - vec3 norm_f; - vec3 cube_c; - vec4 out_f = vec4( 1.0, 1.0, 1.0, 1.0 ); - - // Use the normalmap to shift the refraction - norm_f = ( texture2D( s_norm, shift1 ).xyz); - norm_f += ( texture2D( s_norm, shift2 ).xyz); - norm_f -= 1.0 - ( 16.0 / 1024.0 ); - norm_f = normalize( norm_f ); - - // Reflection/View coordinates - refl_c = ( 1.0 + ( tf_c.xy / tf_c.w ) ) * 0.5; - refl_c.t -= 1.5 * invsurface[2].z / 1080.0; - - // Load reflection and refraction based on our new coords - refl_f = texture2D(s_reflect, refl_c + ( norm_f.st * 0.1 )).rgb; - - cube_c = reflect(normalize(eyevector), norm_f.rgb); - cube_c = cube_c.x * invsurface[0] + cube_c.y * invsurface[1] + cube_c.z * invsurface[2]; - cube_c = (m_model * vec4(cube_c.xyz, 0.0)).xyz; - refr_f = textureCube(s_skycube, cube_c).rgb; - - fres = pow( 1.0 - abs( dot( norm_f, normalize( eyeminusvertex ) ) ), 1.0 ); - out_f.rgb = mix( refr_f, refl_f, fres ); - - gl_FragColor = fog4(out_f); - } -#endif diff --git a/base/src/client/hud.qc b/base/src/client/hud.qc index f2bf22b6..bded0474 100644 --- a/base/src/client/hud.qc +++ b/base/src/client/hud.qc @@ -32,12 +32,15 @@ HUD_Draw(void) iconPos[1] = (hudSize[1] - baseIconSize) - baseIconPadding; /* health, armor icons */ + Font_DrawRText(iconPos + [-((baseIconSize/2) + (baseIconPadding/2)) - baseIconPadding, 0], "100", FONT_16); drawpic(iconPos + [-((baseIconSize/2) + (baseIconPadding/2)), 0], "gfx/hud/health", [baseIconSize, baseIconSize], [1,1,1], 1.0f); + Font_DrawText(iconPos + [(baseIconSize/2) + (baseIconPadding/2) + baseIconSize + baseIconPadding, 0], "100", FONT_16); drawpic(iconPos + [(baseIconSize/2) + (baseIconPadding/2), 0], "gfx/hud/armor", [baseIconSize, baseIconSize], [1,1,1], 1.0f); - /* ammo icon */ - iconPos[0] = (hudSize[0] - baseIconSize) - baseIconPadding; - drawpic(iconPos, "gfx/hud/armor", [baseIconSize, baseIconSize], [1,1,1], 1.0f); + /* little point in not drawing these, even if you don't have a suit */ + if (pl.m_activeWeapon) { + pl.m_activeWeapon.UpdateGUI(); + } Textmenu_Draw(); } diff --git a/base/src/server/defs.h b/base/src/server/defs.h index a1c6292f..cca3ce67 100644 --- a/base/src/server/defs.h +++ b/base/src/server/defs.h @@ -13,5 +13,3 @@ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include "gamerules.h" \ No newline at end of file diff --git a/base/src/server/gamerules_multiplayer.qc b/base/src/server/gamerules_multiplayer.qc index c2e48e4e..c919ebb3 100644 --- a/base/src/server/gamerules_multiplayer.qc +++ b/base/src/server/gamerules_multiplayer.qc @@ -14,114 +14,15 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -void -MultiplayerRules::MultiplayerRules(void) -{ - -} - -void -MultiplayerRules::FrameStart(void) -{ - if (cvar("mp_timelimit")) - if (time >= (cvar("mp_timelimit") * 60)) { - IntermissionStart(); - } - - IntermissionCycle(); -} - -void -MultiplayerRules::PlayerDeath(NSClientPlayer pl) -{ - Plugin_PlayerObituary(g_dmg_eAttacker, g_dmg_eTarget, g_dmg_iWeapon, g_dmg_iHitBody, g_dmg_iDamage); - - /* death-counter */ - pl.deaths++; - forceinfokey(pl, "*deaths", ftos(pl.deaths)); - - /* update score-counter */ - if (pl.flags & FL_CLIENT || pl.flags & FL_MONSTER) - if (g_dmg_eAttacker.flags & FL_CLIENT) { - if (pl == g_dmg_eAttacker) - g_dmg_eAttacker.frags--; - else - g_dmg_eAttacker.frags++; - } - - /* in DM we only care about the frags */ - if (cvar("mp_fraglimit")) - if (g_dmg_eAttacker.frags >= cvar("mp_fraglimit")) { - IntermissionStart(); - } - - pl.SetMovetype(MOVETYPE_NONE); - pl.SetSolid(SOLID_NOT); - pl.SetModelindex(0); - pl.takedamage = DAMAGE_NO; - pl.armor = pl.activeweapon = pl.g_items = 0; - - pl.think = PutClientInServer; - pl.nextthink = time + 4.0f; -} - -void -MultiplayerRules::PlayerSpawn(NSClientPlayer pl) -{ - /* this is where the mods want to deviate */ - entity spot; - - pl.classname = "player"; - pl.health = pl.max_health = 100; - pl.takedamage = DAMAGE_YES; - pl.solid = SOLID_SLIDEBOX; - pl.movetype = MOVETYPE_WALK; - pl.flags = FL_CLIENT; - pl.viewzoom = 1.0; - pl.model = "models/player.mdl"; - setmodel(pl, pl.model); - - setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); - pl.velocity = [0,0,0]; - pl.gravity = __NULL__; - pl.frame = 1; - pl.SendFlags = UPDATE_ALL; - pl.customphysics = Empty; - pl.iBleeds = TRUE; - forceinfokey(pl, "*spec", "0"); - forceinfokey(pl, "*deaths", ftos(pl.deaths)); - - LevelNewParms(); - LevelDecodeParms(pl); - - spot = Spawn_SelectRandom("info_player_deathmatch"); - setorigin(pl, spot.origin); - pl.angles = spot.angles; - - Client_FixAngle(pl, pl.angles); -} - -float -MultiplayerRules::ConsoleCommand(NSClientPlayer pp, string cmd) -{ - tokenize(cmd); - - switch (argv(0)) { - default: - return (0); - } - - return (1); -} - void Game_InitRules(void) { - g_grMode = spawn(MultiplayerRules); - - if (cvar("sv_playerslots") == 1 || cvar("coop") == 1) { - g_grMode = spawn(SingleplayerRules); - } else { - g_grMode = spawn(MultiplayerRules); - } + g_grMode = NSGameRules::InitFromProgs("maps/mp/gametypes/dm.dat"); } + + +void +Game_Worldspawn(void) +{ + +} \ No newline at end of file diff --git a/base/src/server/progs.src b/base/src/server/progs.src index 523c891c..8b74abf0 100644 --- a/base/src/server/progs.src +++ b/base/src/server/progs.src @@ -22,8 +22,6 @@ defs.h ../../../src/botlib/include.src /* mod specific functions */ -gamerules.qc -gamerules_singleplayer.qc gamerules_multiplayer.qc /* global server/shared code */ diff --git a/base/test_maps.pk3dir/maps/test/pbr.bsp b/base/test_maps.pk3dir/maps/test/pbr.bsp new file mode 100644 index 0000000000000000000000000000000000000000..54db985e2a3841b26480a28a233b97615663feb6 GIT binary patch literal 1682648 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 zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBl~ z>IJ^|;)@XSI(171I%Le`q#Ww9ZQ6B6B4-U6o|Nme9XfYUB0u}t{Yg2_o;ss_$1X|a zfd}qQ%5kECgu8`JEhiDT6RUFZ1+_C|6a8sE8DfAZCB6n z4yE0C79^2zQ?j>y`$_5f77ow0YSrnK)H~&j{sWVK;!$<#-nMlcr{AQ@pi-+m4=|bG>tOj=3u5e1F(J^-k$ulzlF1cVo8X zpv-(i?A$S=doJjkebyKf(qkSqYIIiHl91kW?!5EPtnA3FY!i);jx1>lRFUvXK`*I#9@5y;S`XJXwP%qn3w0A z??ausHk@;ZjxEa?bBlj#m%6GSI5K;_A*8QGPaja6)wbb<5&;57i@t@n$x1cWFi4~diib-R z!=&)-FgcWkDdEC!QJ5OO6H;fsCiO1~-wl_B%fjX1ig0E4yYRhmRk*rIt%ks~Fg<)f zl!qCiBFqf4!ZqRAFuNiB|4=$E%*k4gwExTrKd4)dKm2^$5yl+XkKe)d%MZel>u2iL zKYvyI`gco+#{ReV|F_le6pc8&9LeVh9Ma!lfm>P@RLm`3xNv^?yjcbL1vf6Jnmco0 zW%-Ts3tIlDWt3b{HRswn^P|jw!9&h1tb61aHZBxocQR-GH4CO!&MjX&t7`hfMdekC z{*U^aIjelp?0MyjDrO(5l=O|9`DBSvFZ53`*ZvpUkhZe2jbV6TwE`6-4fqD%-lhY<8e8D&a}<+ zndvWnoykYVx<}@9+$K{$(`TlixMHSWrcNf78AB!>zfP|&Gw!&4CLWjLzBA*^RVTj7 z8UJMJ$K|+PY>= 1) ? 0 : 1; diff --git a/src/gs-entbase/server/env_spark.qc b/src/gs-entbase/server/env_spark.qc index 5ed5975d..2eea7036 100644 --- a/src/gs-entbase/server/env_spark.qc +++ b/src/gs-entbase/server/env_spark.qc @@ -134,7 +134,7 @@ env_spark::Spawned(void) { super::Spawned(); - Sound_Precache("fx.spark"); + Sound_Precache("DoSpark"); _m_iSparkParticle = particleeffectnum("fx_spark.main"); } @@ -182,7 +182,7 @@ void env_spark::SparkOnce(void) { if (HasSpawnFlags(EVSPARK_SILENT) == false) { - StartSoundDef("fx.spark", CHAN_AUTO, true); + StartSoundDef("DoSpark", CHAN_AUTO, true); } pointparticles(_m_iSparkParticle, origin, angles, 1); diff --git a/src/gs-entbase/server/func_breakable.qc b/src/gs-entbase/server/func_breakable.qc index ce4c6d44..7c0bf68c 100644 --- a/src/gs-entbase/server/func_breakable.qc +++ b/src/gs-entbase/server/func_breakable.qc @@ -262,9 +262,9 @@ func_breakable::Respawn(void) m_bCanTouch = true; if (HasSpawnFlags(SF_TRIGGER)) { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); } else { - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); } /* initially set the health to that of the ent-data */ @@ -312,7 +312,7 @@ func_breakable::Explode(void) pointparticles(_m_iExplosionParticle, rp, [0,0,0], 1); radiusDamage(GetOrigin(), m_flExplodeMag, 0i, m_flExplodeRad, this); UseTargets(this, TRIG_TOGGLE, 0.0f); /* delay... ignored. */ - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); Disappear(); } @@ -369,7 +369,7 @@ func_breakable::Death(entity inflictor, entity attacker, int damage, vector dir, EntLog("func_breakable (%s) does not have a surfaceproperty for break", funcbreakable_surftable[material]); Disappear(); - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); UseTargets(eActivator, TRIG_TOGGLE, 0.0f); } } diff --git a/src/gs-entbase/server/func_door_rotating.qc b/src/gs-entbase/server/func_door_rotating.qc index f4ef7fcb..534a83c6 100644 --- a/src/gs-entbase/server/func_door_rotating.qc +++ b/src/gs-entbase/server/func_door_rotating.qc @@ -229,7 +229,7 @@ func_door_rotating::Respawn(void) super::Respawn(); #ifdef GS_PHYSICS - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); SetHealth(100); Death = func_door_rotating::Unhinge; #endif @@ -354,7 +354,7 @@ func_door_rotating::SpawnKey(string strKey, string strValue) void func_door_rotating::Unhinge(void) { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); ReleaseThink(); m_bCanTouch = false; SetSolid(SOLID_PHYSICS_BOX); diff --git a/src/gs-entbase/server/func_guntarget.qc b/src/gs-entbase/server/func_guntarget.qc index a096e99a..2148f630 100644 --- a/src/gs-entbase/server/func_guntarget.qc +++ b/src/gs-entbase/server/func_guntarget.qc @@ -215,7 +215,7 @@ func_guntarget::Death(entity inflictor, entity attacker, int damage, vector dir, void func_guntarget::Start(void) { - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); NextPath(); m_iValue = 0; } @@ -223,7 +223,7 @@ func_guntarget::Start(void) void func_guntarget::Stop(void) { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); ClearVelocity(); ReleaseThink(); m_iValue = 1; diff --git a/src/gs-entbase/server/func_physbox.qc b/src/gs-entbase/server/func_physbox.qc index c6b2d906..73f6d61f 100644 --- a/src/gs-entbase/server/func_physbox.qc +++ b/src/gs-entbase/server/func_physbox.qc @@ -91,8 +91,8 @@ void func_physbox::Respawn(void) { super::Respawn(); - health = GetSpawnHealth(); - SetTakedamage(DAMAGE_YES); + SetHealth(GetSpawnFloat("health")); + MakeVulnerable(); SetSolid(SOLID_BBOX); } #endif diff --git a/src/gs-entbase/server/func_pushable.qc b/src/gs-entbase/server/func_pushable.qc index aca404e1..2ca8eac4 100644 --- a/src/gs-entbase/server/func_pushable.qc +++ b/src/gs-entbase/server/func_pushable.qc @@ -175,9 +175,9 @@ func_pushable::Respawn(void) PlayerUse = OnPlayerUse; if (HasSpawnFlags(FNCPUSHABLE_BREAKABLE) == true) { - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); } else { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); } if (!m_eCollBox) { diff --git a/src/gs-entbase/server/func_rot_button.qc b/src/gs-entbase/server/func_rot_button.qc index 54b5663e..efd0d5f6 100644 --- a/src/gs-entbase/server/func_rot_button.qc +++ b/src/gs-entbase/server/func_rot_button.qc @@ -175,7 +175,7 @@ func_rot_button::Respawn(void) ReleaseThink(); if (GetSpawnHealth() > 0) { - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); SetHealth(GetSpawnHealth()); } @@ -277,6 +277,6 @@ func_rot_button::TurnToggle(void) void func_rot_button::Death(entity inflictor, entity attacker, int damage, vector dir, int location) { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); TurnToggle(); } diff --git a/src/gs-entbase/server/prop_physics.qc b/src/gs-entbase/server/prop_physics.qc index e3bd0a89..def351be 100644 --- a/src/gs-entbase/server/prop_physics.qc +++ b/src/gs-entbase/server/prop_physics.qc @@ -14,8 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef PHYSICS_STATIC - enumflags { PHYSPROPSFL_ASLEEP, @@ -95,27 +93,6 @@ prop_physics::Respawn(void) Sleep(); } -#else -class -prop_physics:NSRenderableEntity -{ - void(void) prop_physics; - - virtual void(void) Respawn; -}; - -void -prop_physics::prop_physics(void) -{ -} - -void -prop_physics::Respawn(void) -{ - super::Respawn(); - SetSolid(SOLID_BBOX); -} -#endif CLASSEXPORT(prop_physics_override, prop_physics) CLASSEXPORT(prop_physics_respawnable, prop_physics) diff --git a/src/gs-entbase/server/prop_static.qc b/src/gs-entbase/server/prop_static.qc index f1ab5dcb..db53c59d 100644 --- a/src/gs-entbase/server/prop_static.qc +++ b/src/gs-entbase/server/prop_static.qc @@ -67,6 +67,6 @@ prop_static::Respawn(void) geomtype = GEOMTYPE_TRIMESH; Sleep(); - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); touch = __NULL__; } diff --git a/src/gs-entbase/shared/ambient_generic.qc b/src/gs-entbase/shared/ambient_generic.qc index 1129c9fc..fa69f7a4 100644 --- a/src/gs-entbase/shared/ambient_generic.qc +++ b/src/gs-entbase/shared/ambient_generic.qc @@ -307,7 +307,7 @@ ambient_generic::UseNormal(entity act, triggermode_t state) multicast(origin, MULTICAST_PHS); } else { /* if the file doesn't exist, assume it's a SoundDef */ - if (FileExists(strcat("sound/", m_strActivePath)) == false) { + if (fileExists(strcat("sound/", m_strActivePath)) == false) { Sound_Play(this, CHAN_BODY, m_strActivePath); } else { sound(this, CHAN_BODY, m_strActivePath, m_flVolume, m_flRadius, m_flPitch); diff --git a/src/nav/nodes.qc b/src/nav/nodes.qc index db105753..534fe11f 100644 --- a/src/nav/nodes.qc +++ b/src/nav/nodes.qc @@ -175,7 +175,7 @@ Nodes_Init(void) InitStart(); /* skip if present. TODO: check if they're out of date? */ - if (FileExists(sprintf("data/%s.way", mapname))) { + if (fileExists(sprintf("data/%s.way", mapname))) { NodeEdit_ReadFile(sprintf("%s.way", mapname), true); } else { Nodes_BuildFromEnts(); diff --git a/src/server/NSGameRules.h b/src/server/NSGameRules.h index ad1a36f3..fbdb6d1c 100644 --- a/src/server/NSGameRules.h +++ b/src/server/NSGameRules.h @@ -60,9 +60,9 @@ public: /** Overridable: Called after running physics on the NSClientPlayer in question. */ virtual void PlayerPostFrame(NSClientPlayer); /** Overridable: Called when a NSClientPlayer dies in the game. */ - virtual void PlayerDeath(NSClientPlayer); + virtual void PlayerDeath(NSClientPlayer, NSActor, NSDict); /** Overridable: Called when a NSClientPlayer feels pain. */ - virtual void PlayerPain(NSClientPlayer); + virtual void PlayerPain(NSClientPlayer, NSActor, NSDict); /** Overridable: Called to check if a NSClientPlayer can attack. */ virtual bool PlayerCanAttack(NSClientPlayer); @@ -118,6 +118,8 @@ public: /** Returns the title of the gamemode running. */ virtual string Title(void); + + nonvirtual NSGameRules InitFromProgs(string pathToProgs); /* spectator */ /* @@ -130,6 +132,7 @@ private: float m_flIntermissionTime; float m_flIntermissionCycle; entity m_eIntermissionPoint; + float m_ruleProgs; }; /* our currently running mode */ diff --git a/src/server/NSGameRules.qc b/src/server/NSGameRules.qc index cb5cd510..a70dbc55 100644 --- a/src/server/NSGameRules.qc +++ b/src/server/NSGameRules.qc @@ -60,7 +60,7 @@ NSGameRules::RestoreComplete(void) void NSGameRules::InitPostEnts(void) { - //print("Init!\n"); + RuleC_CallFunc(m_ruleProgs, world, "CodeCallback_StartGameType"); } /* logic */ @@ -77,45 +77,86 @@ NSGameRules::ConsoleCommand(NSClientPlayer pl, string cmd) bool NSGameRules::ClientCommand(NSClient pl, string cmd) { - return (false); + return RuleC_CallString(m_ruleProgs, pl, cmd, "CodeCallback_ClientCommand"); } + bool NSGameRules::ImpulseCommand(NSClient pl, float num) { - return (false); + return RuleC_CallFloat(m_ruleProgs, pl, num, "CodeCallback_ImpulseCommand"); } -/* client */ void NSGameRules::PlayerConnect(NSClientPlayer pl) { - if (Plugin_PlayerConnect(pl) == FALSE) + if (Plugin_PlayerConnect(pl) == false) { bprint(PRINT_HIGH, sprintf("%s^d connected.\n", pl.netname)); + } + + RuleC_CallFunc(m_ruleProgs, pl, "CodeCallback_PlayerConnect"); } void NSGameRules::PlayerDisconnect(NSClientPlayer pl) { bprint(PRINT_HIGH, sprintf("%s^d disconnected.\n", pl.netname)); + RuleC_CallFunc(m_ruleProgs, pl, "CodeCallback_PlayerDisconnect"); } void NSGameRules::PlayerKill(NSClientPlayer pl) { - //Damage_Apply(pl, pl, pl.health, 0, DMG_SKIP_ARMOR); + NSDict damageDecl = spawn(NSDict); + damageDecl.AddKey("damage", itos(1000)); + damageDecl.AddKey("noGod", "1"); + damageDecl.AddKey("noArmor", "1"); + pl.Damage(pl, pl, damageDecl, 1.0, g_vec_null, pl.GetOrigin()); + remove(damageDecl); } void -NSGameRules::PlayerDeath(NSClientPlayer pl) +NSGameRules::PlayerDeath(NSClientPlayer pl, NSActor attacker, NSDict damageDecl) { - /* implemented by sub-class */ + if (RuleC_CallDamage(m_ruleProgs, pl, attacker, attacker, "", "CodeCallback_PlayerKilled")) { + return; + } + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_OBITUARY); + WriteString(MSG_MULTICAST, (attacker.netname) ? attacker.netname : attacker.classname); + WriteString(MSG_MULTICAST, pl.netname); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 0); + msg_entity = world; + multicast([0,0,0], MULTICAST_ALL); + + /* death-counter */ + pl.deaths++; + pl.SetInfoKey("*deaths", ftos(pl.deaths)); + + /* update score-counter */ + if (isPlayer(attacker)) { + if (pl == attacker) { + g_dmg_eAttacker.frags--; + } else { + g_dmg_eAttacker.frags++; + } + } } + void -NSGameRules::PlayerPain(NSClientPlayer pl) +NSGameRules::PlayerPain(NSClientPlayer pl, NSActor attacker, NSDict damageDecl) { - /* implemented by sub-class */ + if (RuleC_CallDamage(m_ruleProgs, pl, attacker, attacker, "", "CodeCallback_PlayerDamage")) { + return; + } + + /* fallback code here, or rest implemented by sub-class */ } + void NSGameRules::PlayerSpawn(NSClientPlayer pl) { + RuleC_CallFunc(m_ruleProgs, pl, "CodeCallback_PlayerSpawn"); + /* implemented by sub-class */ } void @@ -322,9 +363,9 @@ NSGameRules::PlayerCanAttack(NSClientPlayer bp) } bool -NSGameRules::PlayerRequestRespawn(NSClientPlayer bp) +NSGameRules::PlayerRequestRespawn(NSClientPlayer pl) { - return false; + return RuleC_CallRequestSpawn(m_ruleProgs, pl, "CodeCallback_PlayerRequestRespawn"); } void @@ -380,3 +421,44 @@ NSGameRules::Title(void) { return "Default"; } + +NSGameRules +NSGameRules::InitFromProgs(string pathToProgs) +{ + NSGameRules newRule; + + /* No progs .dat, exit out */ + if (fileExists(pathToProgs) == false) { + NSError("Progs at %S does not exist.", pathToProgs); + return (__NULL__); + } + + newRule = spawn(NSGameRules); + newRule.m_ruleProgs = addprogs(pathToProgs); + + if (newRule.m_ruleProgs) { + void(void) mainFunction; + mainFunction = externvalue(newRule.m_ruleProgs, "main"); + + if (mainFunction) { + externset(newRule.m_ruleProgs, this, "self"); + thread(mainFunction()); + } else { + NSError("%S does not have a main function.", pathToProgs); + } + } + + return (newRule); +} + +void +setServerInfo(string serverKey, string setValue) +{ + forceinfokey(world, serverKey, setValue); +} + +string +getServerInfo(string serverKey) +{ + return infokey(world, serverKey); +} diff --git a/src/server/defs.h b/src/server/defs.h index 7312e225..2df9d398 100644 --- a/src/server/defs.h +++ b/src/server/defs.h @@ -26,6 +26,7 @@ #include "vote.h" #include "mapcycle.h" #include "maptweaks.h" +#include "scripts.h" /* helper macros */ #define EVALUATE_FIELD(fieldname, changedflag) {\ diff --git a/src/server/entry.qc b/src/server/entry.qc index 0e86a3f9..039b78cb 100644 --- a/src/server/entry.qc +++ b/src/server/entry.qc @@ -43,22 +43,15 @@ The `self` global is the connecting client in question. void ClientConnect(void) { - int playercount = 0; self.gotData = false; /* don't carry over team settings from a previous session */ forceinfokey(self, "*team", "0"); - /* make sure you never change the classname. ever. */ - if (self.classname != "player") { - EntityDef_SpawnClassname("player"); - } - - if (g_ents_initialized) + if (g_ents_initialized) { + Entity_CreateClass("player"); g_grMode.PlayerConnect((NSClientPlayer)self); - - for (entity a = world; (a = find(a, ::classname, "player"));) - playercount++; + } } /** Called when a player leaves the server. At the end of the function the @@ -77,7 +70,7 @@ ClientDisconnect(void) pl.SetMovetype(MOVETYPE_NONE); pl.SetModelindex(0); pl.SetHealth(0); - pl.SetTakedamage(DAMAGE_NO); + pl.MakeInvulnerable(); pl.SetTeam(0); pl.Disappear(); pl.classname = ""; @@ -142,7 +135,7 @@ PutClientInServer(void) g_grMode.PlayerSpawn((NSClientPlayer)self); /* handle transitions */ - if (FileExists("data/trans.dat")) { + if (fileExists("data/trans.dat")) { for (entity a = world; (a = findfloat(a, ::identity, 1));) { NSEntity levelEnt = (NSEntity)a; @@ -464,18 +457,11 @@ ConsoleCmd(string cmd) NSClientPlayer pl; /* some sv commands can only be executed by a player in-world */ - if ( !self ) { - for ( other = world; ( other = find( other, classname, "player" ) ); ) { - if ( clienttype( other ) == CLIENTTYPE_REAL ) { - self = other; - break; - } - } - } - if (!self) { - for ( other = world; ( other = find( other, classname, "spectator" ) ); ) { - if ( clienttype( other ) == CLIENTTYPE_REAL ) { + other = world; + + while ((other = nextent(other))) { + if (clienttype(other) == CLIENTTYPE_REAL) { self = other; break; } @@ -500,14 +486,17 @@ is being executed. float SV_ShouldPause(float newstatus) { - if (serverkeyfloat("background") == 1) + if (serverkeyfloat("background") == 1) { return (0); + } - if (cvar("pausable") == 1) + if (cvar("pausable") == 1) { return (1); + } - if (cvar("sv_playerslots") > 1) + if (cvar("sv_playerslots") > 1) { return (0); + } return newstatus; } @@ -691,7 +680,6 @@ CheckSpawn(void() spawnfunc) if (MapTweak_EntitySpawn(ent) == true) { ent._mapspawned = true; - ent.classname = desiredClass; } else if (EntityDef_SpawnClassname(desiredClass) != __NULL__) { ent._mapspawned = true; } else if (spawnfunc) { @@ -699,6 +687,8 @@ CheckSpawn(void() spawnfunc) ent.classname = desiredClass; ent._mapspawned = true; g_ent_spawned++; + } else { + ent.identity = 0; } /* check if this entity was meant to spawn on the client-side only */ @@ -729,4 +719,4 @@ CheckSpawn(void() spawnfunc) /* needs to be called at least once after spawn, then any time after. */ ent.Respawn(); -} \ No newline at end of file +} diff --git a/src/server/mapC.h b/src/server/mapC.h index b372f0e2..294aaee1 100644 --- a/src/server/mapC.h +++ b/src/server/mapC.h @@ -25,6 +25,8 @@ #include "mapC_math.h" #include "mapC_weapons.h" +.float deaths; + /** @defgroup mapc MapC @brief MapC/Shared Game-Logic API @ingroup multiprogs @@ -52,6 +54,20 @@ spawnClass(string className, vector desiredPos) return spawnFunc(className, desiredPos); } +bool +changeClass(entity target, string newClass) +{ + bool(entity, string) spawnFunc = externvalue(0, "changeClass"); + return spawnFunc(target, newClass); +} + +bool +cacheEntityDef(string className) +{ + bool(string) spawnFunc = externvalue(0, "EntityDef_Precache"); + return spawnFunc(className); +} + /** Sends an input (See NSIO::Input) to an entity. While you're able to manipulate entities in most ways using bare MapC, you might want to change Nuclide specific attributes of them as well. This can only be done using the I/O system. @@ -65,8 +81,8 @@ For the variety of inputs an entity supports, please look at the respective enti void sendInput(entity target, string inputName, string dataString, entity activator) { - void(entity, entity, string, string) inputFunc = externvalue(0, "sendInput"); - inputFunc(target, activator, inputName, setValue); + void(entity, string, string, entity) inputFunc = externvalue(0, "sendInput"); + inputFunc(target, inputName, dataString, activator); } /** Applies damage to a given entity. @@ -177,11 +193,114 @@ timeToString(int realTime, int zoneType, string formatString) { /* supposed to take extra (optional parameters...) */ if (zoneType == 1i) { - strftime(true, formatString); + return strftime(true, formatString); } else { - strftime(true, formatString); + return strftime(true, formatString); } } -/** @} */ // end of multiprogs - \ No newline at end of file +/** Returns the the value of a console variable. + +@param cvarName specifies the console variable to query. +@return The value in string format. */ +string +getCvar(string cvarName) +{ + return cvar_string(cvarName); +} + +/** Returns the the value of a console variable. + +@param cvarName specifies the console variable to query. +@return The value in integer format. */ +int +getCvarInt(string cvarName) +{ + return (int)cvar(cvarName); +} + +/** Returns the the value of a console variable. + +@param cvarName specifies the console variable to query. +@return The value in integer format. */ +bool +fileExists(string fileName) +{ + bool(string) checkFunc = externvalue(0, "fileExists"); + return checkFunc(fileName); +} + +/** Sets the specified serverinfo key to a set value. + +@param serverKey specifies the serverinfo key to set. +@param setValue specifies the value of said key. */ +void +setServerInfo(string serverKey, string setValue) +{ + void(string, string) checkFunc = externvalue(0, "setServerInfo"); + checkFunc(serverKey, setValue); +} + +/** Returns the the value of a serverinfo key. + +@param serverKey specifies the serverinfo key to query. +@return The value in string format. */ +string +getServerInfo(string serverKey) +{ + string(string) checkFunc = externvalue(0, "getServerInfo"); + return checkFunc(serverKey); +} + +/** Sets the specified userinfo key to a set value. + +@param clientEntity specifies the client to poke. +@param userKey specifies the userinfo key to set. +@param setValue specifies the value of said key. */ +void +setUserInfo(entity clientEntity, string userKey, string setValue) +{ + void(entity, string, string) checkFunc = externvalue(0, "setUserInfo"); + checkFunc(clientEntity, userKey, setValue); +} + +/** Returns the the value of a userinfo key. + +@param clientEntity specifies the client to peek at. +@param userKey specifies the userinfo key to query. +@return The value in string format. */ +string +getUserInfo(entity clientEntity, string userKey) +{ + string(entity, string) checkFunc = externvalue(0, "getUserInfo"); + return checkFunc(clientEntity, userKey); +} + +entity +getSpawnpoint(string className) +{ + entity(string) checkFunc = externvalue(0, "Spawn_SelectRandom"); + return checkFunc(className); +} + +entity +placeSpawnpoint(entity targetEntity) +{ + entity(entity) checkFunc = externvalue(0, "placeSpawnpoint"); + return checkFunc(targetEntity); +} + +void +obituary(string A, string B, string C, string D) +{ + void(string, string, string, string) checkFunc = externvalue(0, "obituary"); + checkFunc(A,B,C,D); +} + +void +damage(entity targetEntity, entity inflictingEntity, entity attackingEntity, int damagePoints, int damageFlags, string meansOfDeath, string weaponDef, vector damageOrigin, vector damageDir, string hitLocation, float timeOffset) +{ + +} + +/** @} */ // end of multiprogs \ No newline at end of file diff --git a/src/server/mapC_math.h b/src/server/mapC_math.h index f68e2e26..af1dabad 100644 --- a/src/server/mapC_math.h +++ b/src/server/mapC_math.h @@ -18,5 +18,4 @@ @brief Server-side plugin wrappers for math related functions. */ -#include "../shared/math.h" -#include "../shared/math_vector.h" \ No newline at end of file +#include "../shared/math.h" \ No newline at end of file diff --git a/src/server/mapcycle.qc b/src/server/mapcycle.qc index d9f44391..599b7b9e 100644 --- a/src/server/mapcycle.qc +++ b/src/server/mapcycle.qc @@ -32,7 +32,7 @@ Mapcycle_Load(string filename) /* read the lines in, see if the map exists and define an enumerated alias */ while ((temp = fgets(fs_mapcycle))) { - if (FileExists(strcat("maps/", temp, ".bsp")) == false) + if (fileExists(strcat("maps/", temp, ".bsp")) == false) continue; localcmd(sprintf("alias m%i \"map %s;alias nextmap m%i\"\n", mapCount, temp, mapCount + 1i)); diff --git a/src/server/maptweaks.qc b/src/server/maptweaks.qc index 52aa4e9e..a9eba526 100644 --- a/src/server/maptweaks.qc +++ b/src/server/maptweaks.qc @@ -162,11 +162,8 @@ MapTweak_Check(int num) static bool MapTweak_FinishSpawn(entity targetEntity, string newClassname) { - entity oldSelf = self; - self = targetEntity; - /* found the edef alternative. */ - if (EntityDef_SpawnClassname(newClassname) != __NULL__) { + if (EntityDef_SwitchClass(targetEntity, newClassname) != __NULL__) { return (true); } @@ -181,7 +178,7 @@ MapTweak_EntitySpawn(entity targetEntity) if (g_mapTweakCount <= 0) { return (false); } - + for (int i = 0; i < g_mapTweakCount; i++) { int segments = tokenize(g_mapTweakTable[i].itemTable); @@ -202,4 +199,4 @@ MapTweak_EntitySpawn(entity targetEntity) } return (false); -} \ No newline at end of file +} diff --git a/src/server/scripts.h b/src/server/scripts.h new file mode 100644 index 00000000..bf7c1497 --- /dev/null +++ b/src/server/scripts.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +void MapC_Init(void); + +void MapC_CallMainFunction(void); + +void MapC_CallNamedFunction(entity, string); + +bool RuleC_CallFunc(float, entity, string); + +bool RuleC_CallDamage(float, entity, entity, entity, string, string); + +bool RuleC_CallRequestSpawn(float, entity, string); + +bool RuleC_CallString(float, entity, string, string); + +bool RuleC_CallFloat(float, entity, float, string); diff --git a/src/server/scripts.qc b/src/server/scripts.qc index 97f349a5..5127e21b 100644 --- a/src/server/scripts.qc +++ b/src/server/scripts.qc @@ -14,6 +14,7 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* Map specific Scripting, nicknamed 'MapC' after TF2's cancelled system */ var float g_mapCProgs; void MapC_Init(void) @@ -24,7 +25,7 @@ MapC_Init(void) mapProgs = sprintf("maps/%s.dat", cvar_string("mapname")); /* No mapname.dat, exit out */ - if (FileExists(mapProgs) == false) { + if (fileExists(mapProgs) == false) { NSError("MapC for level %s at %S does not exist.", mapname, mapProgs); return; } @@ -61,4 +62,110 @@ MapC_CallNamedFunction(entity functionActivator, string targetFunction) } } -/* Sends an input to the specified target. */ \ No newline at end of file +/* Gamerule specific Scripting, nicknamed 'RuleC'. */ +bool +RuleC_CallFunc(float progsNum, entity pl, string funcName) +{ + if (progsNum) { + void(void) mainFunction; + mainFunction = externvalue(progsNum, funcName); + + if (mainFunction) { + entity oldSelf = self; + //externset(progsNum, pl, "self"); + self = pl; + mainFunction(); + self = oldSelf; + return (true); + } + } + + return (false); +} + +bool +RuleC_CallDamage(float progsNum, entity target, entity inflictor, entity attacker, string weapon, string funcName) +{ + if (progsNum) { + void(entity inflictor, entity attacker, string weapon) mainFunction; + mainFunction = externvalue(progsNum, funcName); + + if (mainFunction) { + entity oldSelf = self; + externset(progsNum, target, "self"); + self = target; + mainFunction(inflictor, attacker, weapon); + self = oldSelf; + forceinfokey(target, "*deaths", ftos(target.deaths)); + return (true); + } + } + + return (false); +} + +bool +RuleC_CallRequestSpawn(float progsNum, entity pl, string funcName) +{ + bool returnValue = false; + + if (progsNum) { + bool(void) mainFunction; + mainFunction = externvalue(progsNum, funcName); + + if (mainFunction) { + entity oldSelf = self; + externset(progsNum, pl, "self"); + self = pl; + returnValue = mainFunction(); + self = oldSelf; + return (returnValue); + } + } + + return (returnValue); +} + +bool +RuleC_CallString(float progsNum, entity pl, string targetString, string funcName) +{ + bool returnValue = false; + + if (progsNum) { + bool(string) mainFunction; + mainFunction = externvalue(progsNum, funcName); + + if (mainFunction) { + entity oldSelf = self; + externset(progsNum, pl, "self"); + self = pl; + returnValue = mainFunction(targetString); + self = oldSelf; + return (returnValue); + } + } + + return (returnValue); +} + +bool +RuleC_CallFloat(float progsNum, entity pl, float targetFloat, string funcName) +{ + bool returnValue = false; + + if (progsNum) { + bool(float) mainFunction; + mainFunction = externvalue(progsNum, funcName); + + if (mainFunction) { + entity oldSelf = self; + externset(progsNum, pl, "self"); + self = pl; + returnValue = mainFunction(targetFloat); + self = oldSelf; + return (returnValue); + } + } + + return (returnValue); +} diff --git a/src/server/skill.qc b/src/server/skill.qc index a8ccc337..ffe06746 100644 --- a/src/server/skill.qc +++ b/src/server/skill.qc @@ -31,7 +31,7 @@ Skill_Init(void) Skill_ParseConfig("cfg/skill_manifest.cfg"); - if (FileExists(mapSkillFile)) { + if (fileExists(mapSkillFile)) { Skill_ParseConfig(mapSkillFile); } } @@ -48,10 +48,17 @@ Skill_GetValue(string variable, float defaultValue) { float skill = cvar("skill"); - if (skill == 0) + if (skill == 0) { skill = 2; /* default to medium */ + } float val = fabs(cvar(sprintf("sk_%s%d", variable, skill))); + + /* specific skill variable doesn't exist? */ + if (val == 0) { + val = fabs(cvar(sprintf("sk_%s", variable))); + } + return (val == 0) ? defaultValue : val; } @@ -60,11 +67,18 @@ Skill_GetStringValue(string variable, string defaultValue) { float skill = cvar("skill"); - if (skill == 0) + if (skill == 0) { skill = 2; /* default to medium */ + } string val = cvar_string(sprintf("sk_%s%d", variable, skill)); - return (val == 0) ? defaultValue : val; + + /* specific skill variable doesn't exist? */ + if (val == 0) { + val = cvar_string(sprintf("sk_%s", variable)); + } + + return (val == "") ? defaultValue : val; } bool @@ -85,6 +99,9 @@ Skill_ParseConfig(string fileName) if (argCount == 2i) { if (firstArg == "exec") { Skill_ParseConfig(sprintf("cfg/%s", argv(1))); + } else { + /* some games, like Source, don't use set/seta */ + cvar_set(argv(0), argv(1)); } } else if (argCount == 3i) { if (firstArg == "set" || firstArg == "seta") @@ -94,4 +111,4 @@ Skill_ParseConfig(string fileName) fclose(configFile); return (true); -} \ No newline at end of file +} diff --git a/src/server/vote.qc b/src/server/vote.qc index 6bc9a7a9..20cc8bab 100644 --- a/src/server/vote.qc +++ b/src/server/vote.qc @@ -246,7 +246,7 @@ CSEv_CallVote_s(string text) tokenize(text); switch (argv(0)) { case "map": - if (FileExists(sprintf("maps/%s.bsp", argv(1))) == false) { + if (fileExists(sprintf("maps/%s.bsp", argv(1))) == false) { sprint(self, PRINT_CHAT, sprintf("Map '%s' not available on server.\n", argv(1))); break; } diff --git a/src/shared/NSClientPlayer.h b/src/shared/NSClientPlayer.h index 25e08fd3..24fc3245 100644 --- a/src/shared/NSClientPlayer.h +++ b/src/shared/NSClientPlayer.h @@ -93,6 +93,7 @@ public: /* overrides */ virtual void Save(float); virtual void Restore(string,string); + virtual void Spawned(void); virtual void Respawn(void); virtual void EvaluateEntity(void); virtual float SendEntity(entity,float); @@ -218,3 +219,7 @@ enumflags PLAYER_WEAPONFRAME, PLAYER_CUSTOMFIELDSTART, }; + +#ifdef SERVER +void obituary(string, string, string, string); +#endif diff --git a/src/shared/NSClientPlayer.qc b/src/shared/NSClientPlayer.qc index 79242a40..6f7633d8 100644 --- a/src/shared/NSClientPlayer.qc +++ b/src/shared/NSClientPlayer.qc @@ -28,7 +28,7 @@ NSWeapon GetWeaponEntity(int theclientsnumber) if (wasfreed(e)) e = world; //might have been freed... - return e; + return e; } void @@ -44,6 +44,17 @@ NSClientPlayer::SharedInputFrame(void) if (input_cursor_entitynumber) { NSWeapon nextWeapon = GetWeaponEntity(input_cursor_entitynumber); + /* inpur_cursor_entity is not ours! */ + if (nextWeapon.owner != this) { + return; + } + + if (nextWeapon.IsWeapon() == false) { + return; + } + + input_cursor_entitynumber = 0; + if (nextWeapon != m_activeWeapon) { if (nextWeapon && HasExactItem(nextWeapon)) { m_activeWeapon = nextWeapon; @@ -117,8 +128,10 @@ NSClientPlayer::PreFrame(void) NSItem itemEntry = m_itemList; while (itemEntry) { - itemEntry.PredictPreFrame(); - itemEntry = (NSItem)itemEntry.chain; + if (itemEntry.PredictPreFrame) { + itemEntry.PredictPreFrame(); + } + itemEntry = (NSItem)itemEntry.m_nextItem; } if (vehicle) { @@ -165,8 +178,10 @@ NSClientPlayer::PostFrame(void) NSItem itemEntry = m_itemList; while (itemEntry) { - itemEntry.PredictPostFrame(); - itemEntry = (NSItem)itemEntry.chain; + if (itemEntry.PredictPostFrame) { + itemEntry.PredictPostFrame(); + } + itemEntry = (NSItem)itemEntry.m_nextItem; } setorigin(this, origin); /* update bounds */ @@ -216,7 +231,7 @@ NSClientPlayer::ProcessInput(void) /* handle use button presses */ if (input_buttons & INPUT_USE) - InputUse_Down(); + InputUse_Down(); else InputUse_Up(); @@ -235,7 +250,7 @@ NSClientPlayer::ProcessInput(void) /* can't fire when dead, sorry. */ if (health <= 0) canfire = false; - + if (canfire == false) { if (m_activeWeapon) { m_activeWeapon.Release(); @@ -244,6 +259,10 @@ NSClientPlayer::ProcessInput(void) return; } + if (wasfreed(m_activeWeapon)) { + m_activeWeapon = __NULL__; + } + if (!m_activeWeapon) { return; } @@ -439,8 +458,8 @@ NSClientPlayer::VehicleRelink(void) if (m_activeWeapon != __NULL__ && oldWeapon == __NULL__) { m_activeWeapon.SetOwner(this); - m_activeWeapon._SwitchedToCallback(); m_activeWeapon._AddedCallback(); + m_activeWeapon._SwitchedToCallback(); } } @@ -537,7 +556,7 @@ This is basically CSQC_Input_Frame! So games can override this as they please. */ void NSClientPlayer::ClientInputFrame(void) -{ +{ if (Util_IsPaused()) return; @@ -580,8 +599,9 @@ NSClientPlayer::ClientInputFrame(void) input_buttons |= INPUT_JUMP; } - if (pSeat->m_iHUDWeaponSelected) + if (pSeat->m_iHUDWeaponSelected) { input_cursor_entitynumber = pSeat->m_iHUDWeaponSelected; + } /* IW style stance override */ if (pSeat->m_dForceStance == STANCE_CROUCH) { @@ -678,6 +698,9 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged) if (new) { classname = "player"; + mins = g_pmoveVars.GetStandingMins(); + maxs = g_pmoveVars.GetStandingMaxs(); + Physics_SetViewParms(); } READENTITY_INT(modelindex, PLAYER_MODELINDEX) @@ -691,6 +714,9 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged) READENTITY_FLOAT(m_vecRenderColor[2], PLAYER_MODELINDEX) READENTITY_FLOAT(m_flRenderAmt, PLAYER_MODELINDEX) + READENTITY_ENTNUM(m_flFirstInventoryItem, PLAYER_ITEMS) + READENTITY_ENTNUM(activeweapon, PLAYER_WEAPON) + READENTITY_COORD(origin[0], PLAYER_ORIGIN) READENTITY_COORD(origin[1], PLAYER_ORIGIN) READENTITY_COORD(origin[2], PLAYER_ORIGIN) @@ -713,8 +739,6 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged) READENTITY_INT(vv_flags, PLAYER_FLAGS) READENTITY_INT(gflags, PLAYER_FLAGS) READENTITY_INT(pmove_flags, PLAYER_FLAGS) - READENTITY_ENTNUM(m_flFirstInventoryItem, PLAYER_ITEMS) - READENTITY_ENTNUM(activeweapon, PLAYER_WEAPON) READENTITY_BYTE(weaponframe, PLAYER_WEAPONFRAME) READENTITY_BYTE(health, PLAYER_HEALTH) READENTITY_BYTE(armor, PLAYER_HEALTH) @@ -746,36 +770,25 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged) READENTITY_INT(m_iAmmoTypes[i], PLAYER_AMMOTYPES) } + if (flChanged & PLAYER_SIZE) { + setsize(this, mins, maxs); + } + + if (pSeat->m_ePlayer != this) { + return; + } + #ifdef VALVE if (flChanged & PLAYER_AMMOTYPES) { HUD_AmmoNotify_Check(this); } #endif - /* the entities might not be networked yet, - so by tracking desired entnums and updating - them every time they change is sensible. */ - if (flChanged & PLAYER_VEHICLE) { - vehicle = __NULL__; - } - if (flChanged & PLAYER_WEAPON) { - m_activeWeapon = __NULL__; - } - - if (flChanged & PLAYER_ITEMS) { - m_itemList = __NULL__; - } - - /* only do as often as necessary */ - if ((vehicle_entnum && !vehicle) || (m_flFirstInventoryItem && !m_itemList) || (activeweapon && !m_activeWeapon)) - VehicleRelink(); + VehicleRelink(); PredictPreFrame(); - if (flChanged & PLAYER_SIZE) - setsize(this, mins, maxs); - Relink(); if (flChanged & PLAYER_SPECTATE) { @@ -796,13 +809,18 @@ void NSClientPlayer::PredictPreFrame(void) { if (m_activeWeapon) - m_activeWeapon.PredictPreFrame(); + if (m_activeWeapon.PredictPreFrame) + m_activeWeapon.PredictPreFrame(); SAVE_STATE(modelindex) + SAVE_STATE(colormap) + SAVE_STATE(m_iRenderMode) + SAVE_STATE(m_iRenderFX) + SAVE_STATE(m_vecRenderColor) + SAVE_STATE(m_flRenderAmt) SAVE_STATE(origin) SAVE_STATE(v_angle) SAVE_STATE(angles) - SAVE_STATE(colormap) SAVE_STATE(velocity) SAVE_STATE(basevelocity) SAVE_STATE(grapvelocity) @@ -811,9 +829,8 @@ NSClientPlayer::PredictPreFrame(void) SAVE_STATE(gflags) SAVE_STATE(pmove_flags) SAVE_STATE(m_itemList) - SAVE_STATE(m_activeWeapon) + SAVE_STATE(activeweapon) SAVE_STATE(weaponframe) - SAVE_STATE(g_items) SAVE_STATE(health) SAVE_STATE(armor) SAVE_STATE(mins) @@ -834,6 +851,9 @@ NSClientPlayer::PredictPreFrame(void) SAVE_STATE(spec_mode) SAVE_STATE(spec_flags) + SAVE_STATE(m_itemList) + SAVE_STATE(m_activeWeapon) + for (int i = 0i; i < MAX_AMMO_TYPES; i++) { m_iAmmoTypes_net[i] = m_iAmmoTypes[i]; } @@ -853,10 +873,14 @@ void NSClientPlayer::PredictPostFrame(void) { ROLL_BACK(modelindex) + ROLL_BACK(colormap) + ROLL_BACK(m_iRenderMode) + ROLL_BACK(m_iRenderFX) + ROLL_BACK(m_vecRenderColor) + ROLL_BACK(m_flRenderAmt) ROLL_BACK(origin) ROLL_BACK(v_angle) ROLL_BACK(angles) - ROLL_BACK(colormap) ROLL_BACK(velocity) ROLL_BACK(basevelocity) ROLL_BACK(grapvelocity) @@ -865,9 +889,8 @@ NSClientPlayer::PredictPostFrame(void) ROLL_BACK(gflags) ROLL_BACK(pmove_flags) ROLL_BACK(m_itemList) - ROLL_BACK(m_activeWeapon) + ROLL_BACK(activeweapon) ROLL_BACK(weaponframe) - ROLL_BACK(g_items) ROLL_BACK(health) ROLL_BACK(armor) ROLL_BACK(mins) @@ -888,12 +911,19 @@ NSClientPlayer::PredictPostFrame(void) ROLL_BACK(spec_mode) ROLL_BACK(spec_flags) + ROLL_BACK(m_itemList) + ROLL_BACK(m_activeWeapon) + + VehicleRelink(); + for (int i = 0i; i < MAX_AMMO_TYPES; i++) { m_iAmmoTypes[i] = m_iAmmoTypes_net[i]; } if (m_activeWeapon) { - m_activeWeapon.PredictPostFrame(); + if (m_activeWeapon.PredictPostFrame) { + m_activeWeapon.PredictPostFrame(); + } } } #else @@ -1054,7 +1084,7 @@ NSClientPlayer::Respawn(void) /* ================= NSClientPlayer::MakeTempSpectator - + This is what dead players in round matches become, or when we spawn for the first time before selecting a loadout or something. ================= @@ -1067,7 +1097,7 @@ NSClientPlayer::MakeTempSpectator(void) SetModelindex(0); SetSolid(SOLID_NOT); SetMovetype(MOVETYPE_NOCLIP); - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); maxspeed = 250; AddVFlags(VFL_FAKESPEC); max_health = health = 0; @@ -1094,34 +1124,24 @@ NSClientPlayer::MakeTempSpectator(void) /* ================= NSClientPlayer::MakeDead - + Sets all the appropriate attributes to make sure we're dead ================= */ void NSClientPlayer::Death(entity inflictor, entity attacker, int damage, vector dir, int location) { + RemoveAllItems(false); classname = "player"; health = max_health = 0; armor = 0; g_items = 0; - - if (m_activeWeapon) { - m_activeWeapon.Destroy(); - m_activeWeapon = __NULL__; - } - - if (m_itemList) { - m_itemList.Destroy(); - m_itemList = __NULL__; - } - effects = 0; alpha = 1.0f; SetModelindex(0); SetMovetype(MOVETYPE_NONE); SetSolid(SOLID_NOT); - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); viewzoom = 1.0; view_ofs = [0,0,0]; vehicle = __NULL__; @@ -1136,10 +1156,17 @@ NSClientPlayer::Death(entity inflictor, entity attacker, int damage, vector dir, m_flDeathTime = time; } +void +NSClientPlayer::Spawned(void) +{ + super::Spawned(); + MakePlayer(); +} + /* ================= NSClientPlayer::MakePlayer - + True participating player, can walk around and everything. ================= */ @@ -1159,16 +1186,18 @@ NSClientPlayer::MakePlayer(void) alpha = 1.0f; SetSolid(SOLID_SLIDEBOX); SetMovetype(MOVETYPE_WALK); - SetTakedamage(DAMAGE_YES); - SetVelocity([0,0,0]); + MakeVulnerable(); + SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + ClearVelocity(); viewzoom = 1.0; vehicle = __NULL__; SetGravity(1.0f); SendFlags = UPDATE_ALL; customphysics = Empty; EnableBleeding(); - scale = 1.0f; - SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + SetScale(1.0f); + Physics_SetViewParms(); + SetSize(g_pmoveVars.GetStandingMins(), g_pmoveVars.GetStandingMaxs()); forceinfokey(this, "*spectator", "0"); forceinfokey(this, "*deaths", ftos(deaths)); forceinfokey(this, "*dead", "0"); @@ -1181,7 +1210,7 @@ NSClientPlayer::MakeSpectator(void) ClientKill(); MakeTempSpectator(); team = 0; - forceinfokey(this, "*team", ftos(team)); + forceinfokey(this, "*team", ftos(team)); } /* @@ -1200,15 +1229,12 @@ NSClientPlayer::EvaluateEntity(void) EVALUATE_FIELD(modelindex, PLAYER_MODELINDEX) EVALUATE_FIELD(colormap, PLAYER_MODELINDEX) - - /* crap the render mode updates in here */ EVALUATE_FIELD(m_iRenderMode, PLAYER_MODELINDEX) EVALUATE_FIELD(m_iRenderFX, PLAYER_MODELINDEX) EVALUATE_VECTOR(m_vecRenderColor, 0, PLAYER_MODELINDEX) EVALUATE_VECTOR(m_vecRenderColor, 1, PLAYER_MODELINDEX) EVALUATE_VECTOR(m_vecRenderColor, 2, PLAYER_MODELINDEX) EVALUATE_FIELD(m_flRenderAmt, PLAYER_MODELINDEX) - EVALUATE_VECTOR(origin, 0, PLAYER_ORIGIN) EVALUATE_VECTOR(origin, 1, PLAYER_ORIGIN) EVALUATE_VECTOR(origin, 2, PLAYER_ORIGIN) @@ -1299,6 +1325,9 @@ NSClientPlayer::SendEntity(entity ePEnt, float flChanged) SENDENTITY_FLOAT(m_vecRenderColor[2], PLAYER_MODELINDEX) SENDENTITY_FLOAT(m_flRenderAmt, PLAYER_MODELINDEX) + SENDENTITY_ENTITY(m_itemList, PLAYER_ITEMS) + SENDENTITY_ENTITY(m_activeWeapon, PLAYER_WEAPON) + SENDENTITY_COORD(origin[0], PLAYER_ORIGIN) SENDENTITY_COORD(origin[1], PLAYER_ORIGIN) SENDENTITY_COORD(origin[2], PLAYER_ORIGIN) @@ -1321,8 +1350,6 @@ NSClientPlayer::SendEntity(entity ePEnt, float flChanged) SENDENTITY_INT(vv_flags, PLAYER_FLAGS) SENDENTITY_INT(gflags, PLAYER_FLAGS) SENDENTITY_INT(pmove_flags, PLAYER_FLAGS) - SENDENTITY_ENTITY(m_itemList, PLAYER_ITEMS) - SENDENTITY_ENTITY(m_activeWeapon, PLAYER_WEAPON) SENDENTITY_BYTE(weaponframe, PLAYER_WEAPONFRAME) SENDENTITY_BYTE(health, PLAYER_HEALTH) SENDENTITY_BYTE(armor, PLAYER_HEALTH) @@ -1439,7 +1466,7 @@ NSClientPlayer::InputUse_Down(void) } else if (!(vv_flags & VFL_USE_RELEASED)) { return; } - + vector vecSource; entity eRad; bool found_use = false; @@ -1528,7 +1555,7 @@ NSClientPlayer::Footsteps_Update(void) StartSoundDef("Player.Wade", CHAN_BODY, true); step_time = time + 2.0f; return; - } else if (waterlevel == 3) { + } else if (waterlevel == 3) { StartSoundDef("Player.Swim", CHAN_BODY, true); step_time = time + 2.0f; return; @@ -1584,31 +1611,27 @@ void NSClientPlayer::Damage(entity inflictor, entity attacker, NSDict damageDecl, float damageScale, vector dmgDir, vector hitLocation) { #ifdef SERVER - super::Damage(inflictor, attacker, damageDecl, damageScale, dmgDir, hitLocation); - -#if 0 + float armorDamage = 0; bool isFriendlyFire = false; - - /* Damage */ - NSSurfacePropEntity eTarget = (NSSurfacePropEntity)t; + string damageString = ReadString(damageDecl.GetString("damage")); + float damagePoints = (float)rint(stof(damageString) * damageScale); + NSSurfacePropEntity ourAttacker = (NSSurfacePropEntity)attacker; /* sanity check */ - if (t.takedamage == DAMAGE_NO) + if (isAlive(this) == false) { return; - - /* for armor damage */ - float flArmor = 0; - float flNewDamage = 0; + } /* player god mode */ - if (eTarget.flags & FL_CLIENT && eTarget.flags & FL_GODMODE) + if (damageDecl.GetBool("noGod") == false && isGodMode(this)) { return; + } /* friendly fire check */ - if (t != c) { - if (IsTeamplay()) { - if (t.flags & FL_CLIENT && c.flags & FL_CLIENT) { - if (t.team == c.team) { + if (this != attacker) { + if (g_grMode.IsTeamplay()) { + if (flags & FL_CLIENT && attacker.flags & FL_CLIENT) { + if (team == attacker.team) { if (autocvar_sv_friendlyFire == false) { return; } else { @@ -1620,66 +1643,61 @@ NSClientPlayer::Damage(entity inflictor, entity attacker, NSDict damageDecl, flo } /* already dead, please avoid recursion */ - if (eTarget.GetHealth() <= 0) + if (GetHealth() <= 0) { return; + } - /* before any calculation is done... */ - g_dmg_iRealDamage = dmg; + /* don't allow any damage */ + if (g_grMode.PlayerCanAttack(this) == false) { + return; + } - /* only clients have armor */ - if (eTarget.flags & FL_CLIENT) { - NSClientPlayer tp = (NSClientPlayer)t; + /* skip armor */ + if (damageDecl.GetBool("noArmor") == false) { + if (armor && damagePoints > 0) { + float postArmorDamage = 0; + float armorRatio = GetDefFloat("armorProtection"); + float armorBonus = GetDefFloat("armorBonus"); - /* don't allow any damage */ - if (PlayerCanAttack(tp) == false) { - g_dmg_eAttacker = (NSEntity)c; - g_dmg_eTarget = (NSEntity)t; - g_dmg_iDamage = 0; - g_dmg_iHitBody = 0; - g_dmg_iFlags = type; - g_dmg_iWeapon = w; - return; - } - - /* skip armor */ - if not (type & DMG_SKIP_ARMOR) - if (tp.armor && dmg > 0) { - - flNewDamage = dmg * 0.2; - flArmor = (dmg - flNewDamage) * 0.5; - - if (flArmor > tp.armor) { - flArmor = tp.armor; - flArmor *= (1/0.5); - flNewDamage = dmg - flArmor; - tp.armor = 0; - } else { - tp.armor -= flArmor; + /* amount of damage the armor absorbs */ + if (armorRatio <= 0.0) { + armorRatio = 0.2; } - dmg = flNewDamage; + + /* armor point cost for health point */ + if (armorBonus <= 0.0) { + armorBonus = 1.0; + } + + /* damage to be applied to health value */ + postArmorDamage = (damagePoints * armorRatio); + + /* figure out how much to deduct from armor. */ + armorDamage = (damagePoints - postArmorDamage) * armorBonus; + + /* to-deduct value exceeds armor value */ + if (armorDamage > armor) { + /* armor points left will cushion whatever damage they can */ + armorDamage = armor * (1.0 / armorBonus); + armor = 0; + postArmorDamage = (damagePoints - armorDamage); + } else { + armor -= armorDamage; + } + + damagePoints = postArmorDamage; } } - dmg = rint(dmg); - eTarget.SetHealth(eTarget.GetHealth() - dmg); - - /* the globals... */ - g_dmg_eAttacker = (NSEntity)c; - g_dmg_eTarget = (NSEntity)t; - g_dmg_iDamage = dmg; - g_dmg_iHitBody = trace_surface_id; - g_dmg_iFlags = type; - g_dmg_iWeapon = w; - - NSLog("%S does %d dmg to %S (body: %d, flags: %i, weapon: %i)", - c.classname, dmg, t.classname, g_dmg_iHitBody, g_dmg_iFlags, g_dmg_iWeapon); + damagePoints = rint(damagePoints); + SetHealth(GetHealth() - damagePoints); /* friendly fire penalty */ if (isFriendlyFire) { int lastDmg = 0i; - NSClientPlayer plC = (NSClientPlayer)c; + NSClientPlayer plC = (NSClientPlayer)attacker; lastDmg = plC.m_iFriendlyDMG; - plC.m_iFriendlyDMG += dmg; + plC.m_iFriendlyDMG += damagePoints; /* kick the client. */ if (plC.m_iFriendlyDMG >= autocvar_mp_td_dmgToKick) { @@ -1692,13 +1710,13 @@ NSClientPlayer::Damage(entity inflictor, entity attacker, NSDict damageDecl, flo } } - bprint(PRINT_CHAT, sprintf("%s ^7attacked a teammate.\n", c.netname)); + bprint(PRINT_CHAT, sprintf("%s ^7attacked a teammate.\n", attacker.netname)); } - if (dmg > 0 || flArmor > 0) { + if (damagePoints > 0 || armorDamage > 0) { vector dmg_origin; - if (c.origin == [0,0,0]) + if (attacker.origin == [0,0,0]) dmg_origin = g_dmg_eTarget.origin; else dmg_origin = g_dmg_eAttacker.origin; @@ -1708,7 +1726,7 @@ NSClientPlayer::Damage(entity inflictor, entity attacker, NSDict damageDecl, flo WriteCoord(MSG_MULTICAST, dmg_origin[0]); WriteCoord(MSG_MULTICAST, dmg_origin[1]); WriteCoord(MSG_MULTICAST, dmg_origin[2]); - WriteInt(MSG_MULTICAST, g_dmg_iRealDamage); + WriteInt(MSG_MULTICAST, damagePoints); WriteInt(MSG_MULTICAST, g_dmg_iFlags); msg_entity = g_dmg_eTarget; multicast([0,0,0], MULTICAST_ONE_R); @@ -1720,25 +1738,28 @@ NSClientPlayer::Damage(entity inflictor, entity attacker, NSDict damageDecl, flo if ((g_dmg_eAttacker.flags & FL_CLIENT) && (g_dmg_eTarget != g_dmg_eAttacker)) { WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, EV_HITNOTIFY); - msg_entity = c; + msg_entity = attacker; multicast([0,0,0], MULTICAST_ONE); } } + damageDecl.AddKey("location", vtos(hitLocation)); + /* they died */ - if (eTarget.GetHealth() <= 0) { - if (eTarget.flags & FL_CLIENT) { - PlayerDeath(eTarget); - } else { - eTarget.Death(); - } + if (GetHealth() <= 0) { + g_grMode.PlayerDeath(this, attacker, damageDecl); + Death(inflictor, attacker, (int)damagePoints, dmgDir, 0i); } else { - if (eTarget.flags & FL_CLIENT) { - PlayerPain(eTarget); - } else { - eTarget.Pain(); - } + g_grMode.PlayerPain(this, attacker, damageDecl); + Pain(inflictor, attacker, (int)damagePoints, dmgDir, 0i); } #endif +} + +#ifdef SERVER +void +obituary(string targetName, string attackerName, string weaponDef, string meansOfDeath) +{ + +} #endif -} \ No newline at end of file diff --git a/src/shared/NSEntity.h b/src/shared/NSEntity.h index a62b8f79..773f5255 100644 --- a/src/shared/NSEntity.h +++ b/src/shared/NSEntity.h @@ -76,9 +76,6 @@ public: virtual void SpawnKey(string,string); virtual void Spawned(void); - /** Handles what happens before the entity gets removed from the client game. */ - virtual void OnRemoveEntity(void); - /** Tells the engine to make the entity static, effectively making it inaccessible. It will be removed from the game-logic but remain visible and it will retain its collision and maintain the appearance it had before getting removed. */ @@ -291,8 +288,6 @@ public: /** When called it'll make the entity uninteractable (but not destroy it). It will hide it, as well as remove any collision associated with it. */ nonvirtual void Disappear(void); - /** When called, will remove the entity from the game entirely. */ - nonvirtual void Destroy(void); /** Call this if you want to update bounding boxes to take angles into account. */ nonvirtual void UpdateBounds(void); diff --git a/src/shared/NSEntity.qc b/src/shared/NSEntity.qc index 034fc925..0a58af9f 100644 --- a/src/shared/NSEntity.qc +++ b/src/shared/NSEntity.qc @@ -48,7 +48,7 @@ NSEntity::Spawned(void) } /* used to network our shared ID */ - entityDefID = EntityDef_IDFromName(classname); + entityDefID = EntityDef_NetIDFromName(classname); #endif } @@ -1141,26 +1141,6 @@ NSEntity::SpawnKey(string strKey, string strValue) } } -void -NSEntity::OnRemoveEntity(void) -{ -} - -void -NSEntity::Destroy(void) -{ - removed = 1; /* mark this as cleanly removed */ - OnRemoveEntity(); - customphysics = __NULL__; - modelindex = 0; - solid = 0; - movetype = 0; - classname = 0; - think = __NULL__; - nextthink = 0.0f; - remove(this); -} - void NSEntity::Show(void) { @@ -1497,6 +1477,14 @@ spawnClass(string className, vector desiredPos) return (newEntity); } +#ifdef SERVER +bool +changeClass(entity target, string className) +{ + return EntityDef_SwitchClass((NSEntity)target, className) ? (true) : (false); +} +#endif + void sendInput(entity target, string inputName, string dataString, entity activator) { @@ -1572,8 +1560,46 @@ bool isBot(entity entityToCheck) { #ifdef SERVER - return (clienttype(self) == CLIENTTYPE_BOT) ? (true) : (false); + return (clienttype(entityToCheck) == CLIENTTYPE_BOT) ? (true) : (false); #else return (false); #endif } + + +#ifdef SERVER +void +setUserInfo(entity clientEntity, string serverKey, string setValue) +{ + if (isClient(clientEntity)) { + NSClient client = (NSClient) clientEntity; + client.SetInfoKey(serverKey, setValue); + } +} + +string +getUserInfo(entity clientEntity, string serverKey) +{ + if (isClient(clientEntity)) { + NSClient client = (NSClient) clientEntity; + return client.GetInfoKey(serverKey); + } +} +#endif + +void +placeSpawnpoint(entity target) +{ + vector testorg = target.origin; + + for (int i = 0; i < 32; i++) { + tracebox(testorg, target.mins, target.maxs, testorg, MOVE_NORMAL, target); + + if (!trace_startsolid) { + setorigin(target, testorg); + break; + } + + testorg[2] += 1.0; + } +} diff --git a/src/shared/NSIO.h b/src/shared/NSIO.h index 5577d3df..602b1b4d 100644 --- a/src/shared/NSIO.h +++ b/src/shared/NSIO.h @@ -70,6 +70,11 @@ public: /** Like GetDefVector, but queries a specified def, falling back to reading from our own if it's not defined. */ nonvirtual vector GetSubDefVector(string, string); + /** When called, will remove the entity from the game entirely. */ + nonvirtual void Destroy(void); + /** Handles what happens before the entity gets removed from the client game. */ + virtual void OnRemoveEntity(void); + #ifdef SERVER /** Handles saving a copy of this entity to a given filehandle. Within you want to use the NSIO::SaveFloat() etc. methods to write diff --git a/src/shared/NSIO.qc b/src/shared/NSIO.qc index e6d3a761..9e80945f 100644 --- a/src/shared/NSIO.qc +++ b/src/shared/NSIO.qc @@ -21,7 +21,7 @@ NSIO::NSIO(void) /* Not in Deathmatch */ if (spawnflags & 2048) { if (cvar("sv_playerslots") > 1) { - remove(this); + Destroy(); return; } } @@ -33,6 +33,8 @@ NSIO::NSIO(void) m_strOnUser3 = m_strOnUser4 = __NULL__; #else + owner = __NULL__; + chain = __NULL__; isCSQC = TRUE; effects |= EF_NOSHADOW; #endif @@ -59,6 +61,26 @@ NSIO::Spawned(void) #endif } +void +NSIO::OnRemoveEntity(void) +{ +} + +void +NSIO::Destroy(void) +{ + removed = 1; /* mark this as cleanly removed */ + OnRemoveEntity(); + customphysics = __NULL__; + modelindex = 0; + solid = 0; + movetype = 0; + classname = 0; + think = __NULL__; + nextthink = 0.0f; + remove(this); +} + float NSIO::GetDefAct(string keyName) { @@ -77,7 +99,7 @@ NSIO::GetDefAct(string keyName) string NSIO::GetDefString(string keyName) { - return EntityDef_GetKeyValue(classname, keyName); + return ReadString(EntityDef_GetKeyValue(declclass, keyName)); } vector @@ -109,12 +131,12 @@ NSIO::GetSubDefAct(string subDef, string keyName) { float actCount; string fireValue = GetSubDefString(subDef, keyName); - - /* not in className either. make it known */ - if (subDef == "") { + + /* not in classname either. make it known */ + if (fireValue == "") { return (-1); } - + actCount = tokenizebyseparator(fireValue, ","); if (actCount == 1) { @@ -128,11 +150,12 @@ NSIO::GetSubDefAct(string subDef, string keyName) string NSIO::GetSubDefString(string subDef, string keyName) { - string fireValue = EntityDef_GetKeyValue(subDef, keyName); + string fireDefString = EntityDef_GetKeyValue(subDef, keyName); + string fireValue = ReadString(fireDefString); /* if not in FireInfo, check classname Def */ if (fireValue == "") { - fireValue = EntityDef_GetKeyValue(classname, keyName); + fireValue = GetDefString(keyName); } return (fireValue); @@ -188,15 +211,7 @@ string NSIO::ReadString(string inputString) { if (inputString && inputString != "") { - -#ifdef SERVER - /* is this supposed to be read from a skill cvar? */ - if (substring(inputString, 0, 6) == "skill:") { - return Skill_GetStringValue(substring(inputString, 6, -1), ""); - } -#endif - - return Constants_LookUp(inputString, inputString); + return unpackStringCommand(inputString); } return ""; @@ -250,7 +265,7 @@ NSIO::GetSpawnString(string keyName) } } - return ReadString(EntityDef_GetKeyValue(classname, keyName)); + return ReadString(EntityDef_GetKeyValue(declclass, keyName)); } vector diff --git a/src/shared/NSItem.h b/src/shared/NSItem.h index 64978a61..1b462727 100644 --- a/src/shared/NSItem.h +++ b/src/shared/NSItem.h @@ -113,6 +113,7 @@ public: #endif virtual bool IsWeapon(void); + virtual bool InInventory(void); private: /** Called to signal that the owner added this weapon to their inventory. */ @@ -146,6 +147,7 @@ private: NETWORKED_FLOAT(owner_entnum) #endif - entity chain_net; + NSItem m_nextItem; + NSItem m_nextItem_net; entity owner_net; }; diff --git a/src/shared/NSItem.qc b/src/shared/NSItem.qc index 17987376..91fd8a1d 100644 --- a/src/shared/NSItem.qc +++ b/src/shared/NSItem.qc @@ -17,7 +17,6 @@ void NSItem::NSItem(void) { -#ifdef SERVER m_iClip = -1; m_iWasDropped = 0i; m_iInvItem = 0i; @@ -30,7 +29,8 @@ NSItem::NSItem(void) m_bNoTouch = false; m_bInvCarry = false; m_strRequires = __NULL__; -#endif + m_nextItem = __NULL__; + owner = __NULL__; } #ifdef SERVER @@ -75,7 +75,12 @@ NSItem::SpawnKey(string strKey, string strValue) if (substring(strKey, 0, 8) == "inv_ammo") { string ammoType = substring(strKey, 4, -1); int ammoID = ammoNumForName(ammoType); - m_GiveAmmo[ammoID] = stoi(strValue); + + if (ammoID != -1) { + m_GiveAmmo[ammoID] = stoi(unpackStringCommand(strValue)); + } else { + NSError("Item %S tries to give invalid ammo type %S", declclass, ammoType); + } return; } @@ -315,45 +320,47 @@ NSItem::BecomePickup(void) float NSItem::SendEntity(entity ePEnt, float flChanged) { + /* item is in somebody elses inventory. */ + if (InInventory() == true && ePEnt != GetOwner()) { + return (false); + } + WriteByte(MSG_ENTITY, ENT_ITEM); /* broadcast how much data is expected to be read */ WriteFloat(MSG_ENTITY, flChanged); - if (1) { - SENDENTITY_COORD(origin[0], ITEMFL_CHANGED_ORIGIN_X) - SENDENTITY_COORD(origin[1], ITEMFL_CHANGED_ORIGIN_Y) - SENDENTITY_COORD(origin[2], ITEMFL_CHANGED_ORIGIN_Z) - SENDENTITY_ANGLE(angles[0], ITEMFL_CHANGED_ANGLES_X) - SENDENTITY_ANGLE(angles[1], ITEMFL_CHANGED_ANGLES_Y) - SENDENTITY_ANGLE(angles[2], ITEMFL_CHANGED_ANGLES_Z) - SENDENTITY_SHORT(entityDefID, ITEMFL_CHANGED_MODELINDEX) - SENDENTITY_SHORT(modelindex, ITEMFL_CHANGED_MODELINDEX) - SENDENTITY_BYTE(solid, ITEMFL_CHANGED_SOLID) - SENDENTITY_BYTE(movetype, ITEMFL_CHANGED_FLAGS) - SENDENTITY_INT(flags, ITEMFL_CHANGED_FLAGS) - SENDENTITY_INT(vv_flags, ITEMFL_CHANGED_FLAGS) - SENDENTITY_COORD(mins[0], ITEMFL_CHANGED_SIZE) - SENDENTITY_COORD(mins[1], ITEMFL_CHANGED_SIZE) - SENDENTITY_COORD(mins[2], ITEMFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[0], ITEMFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[1], ITEMFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[2], ITEMFL_CHANGED_SIZE) - SENDENTITY_BYTE(frame, ITEMFL_CHANGED_FRAME) - SENDENTITY_FLOAT(skin, ITEMFL_CHANGED_SKIN) - SENDENTITY_FLOAT(effects, ITEMFL_CHANGED_EFFECTS) - SENDENTITY_FLOAT(scale, ITEMFL_CHANGED_SCALE) - SENDENTITY_COORD(velocity[0], ITEMFL_CHANGED_VELOCITY_X) - SENDENTITY_COORD(velocity[1], ITEMFL_CHANGED_VELOCITY_Y) - SENDENTITY_COORD(velocity[2], ITEMFL_CHANGED_VELOCITY_Z) - SENDENTITY_COORD(avelocity[0], ITEMFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_COORD(avelocity[1], ITEMFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_COORD(avelocity[2], ITEMFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_ENTITY(chain, ITEMFL_CHANGED_CHAIN) - SENDENTITY_ENTITY(owner, ITEMFL_CHANGED_CHAIN) - } else { - /* the item is in the inventory */ - } + SENDENTITY_ENTITY(m_nextItem, ITEMFL_CHANGED_CHAIN) + SENDENTITY_ENTITY(owner, ITEMFL_CHANGED_CHAIN) + SENDENTITY_COORD(origin[0], ITEMFL_CHANGED_ORIGIN_X) + SENDENTITY_COORD(origin[1], ITEMFL_CHANGED_ORIGIN_Y) + SENDENTITY_COORD(origin[2], ITEMFL_CHANGED_ORIGIN_Z) + SENDENTITY_ANGLE(angles[0], ITEMFL_CHANGED_ANGLES_X) + SENDENTITY_ANGLE(angles[1], ITEMFL_CHANGED_ANGLES_Y) + SENDENTITY_ANGLE(angles[2], ITEMFL_CHANGED_ANGLES_Z) + SENDENTITY_FLOAT(entityDefID, ITEMFL_CHANGED_MODELINDEX) + SENDENTITY_SHORT(modelindex, ITEMFL_CHANGED_MODELINDEX) + SENDENTITY_BYTE(solid, ITEMFL_CHANGED_SOLID) + SENDENTITY_BYTE(movetype, ITEMFL_CHANGED_FLAGS) + SENDENTITY_INT(flags, ITEMFL_CHANGED_FLAGS) + SENDENTITY_INT(modelflags, ITEMFL_CHANGED_FLAGS) + SENDENTITY_INT(vv_flags, ITEMFL_CHANGED_FLAGS) + SENDENTITY_COORD(mins[0], ITEMFL_CHANGED_SIZE) + SENDENTITY_COORD(mins[1], ITEMFL_CHANGED_SIZE) + SENDENTITY_COORD(mins[2], ITEMFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[0], ITEMFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[1], ITEMFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[2], ITEMFL_CHANGED_SIZE) + SENDENTITY_BYTE(frame, ITEMFL_CHANGED_FRAME) + SENDENTITY_FLOAT(skin, ITEMFL_CHANGED_SKIN) + SENDENTITY_FLOAT(effects, ITEMFL_CHANGED_EFFECTS) + SENDENTITY_FLOAT(scale, ITEMFL_CHANGED_SCALE) + SENDENTITY_COORD(velocity[0], ITEMFL_CHANGED_VELOCITY_X) + SENDENTITY_COORD(velocity[1], ITEMFL_CHANGED_VELOCITY_Y) + SENDENTITY_COORD(velocity[2], ITEMFL_CHANGED_VELOCITY_Z) + SENDENTITY_COORD(avelocity[0], ITEMFL_CHANGED_ANGULARVELOCITY) + SENDENTITY_COORD(avelocity[1], ITEMFL_CHANGED_ANGULARVELOCITY) + SENDENTITY_COORD(avelocity[2], ITEMFL_CHANGED_ANGULARVELOCITY) return (1); } @@ -361,6 +368,8 @@ NSItem::SendEntity(entity ePEnt, float flChanged) void NSItem::EvaluateEntity(void) { + EVALUATE_FIELD(m_nextItem, ITEMFL_CHANGED_CHAIN) + EVALUATE_FIELD(owner, ITEMFL_CHANGED_CHAIN) EVALUATE_VECTOR(origin, 0, ITEMFL_CHANGED_ORIGIN_X) EVALUATE_VECTOR(origin, 1, ITEMFL_CHANGED_ORIGIN_Y) EVALUATE_VECTOR(origin, 2, ITEMFL_CHANGED_ORIGIN_Z) @@ -368,9 +377,11 @@ NSItem::EvaluateEntity(void) EVALUATE_VECTOR(angles, 1, ITEMFL_CHANGED_ANGLES_Y) EVALUATE_VECTOR(angles, 2, ITEMFL_CHANGED_ANGLES_Z) EVALUATE_FIELD(modelindex, ITEMFL_CHANGED_MODELINDEX) + EVALUATE_FIELD(entityDefID, ITEMFL_CHANGED_MODELINDEX) EVALUATE_FIELD(solid, BASEFL_CHANGED_MODELINDEX) EVALUATE_FIELD(movetype, ITEMFL_CHANGED_FLAGS) EVALUATE_FIELD(flags, ITEMFL_CHANGED_FLAGS) + EVALUATE_FIELD(modelflags, ITEMFL_CHANGED_FLAGS) EVALUATE_FIELD(vv_flags, ITEMFL_CHANGED_FLAGS) EVALUATE_VECTOR(mins, 0, ITEMFL_CHANGED_SIZE) EVALUATE_VECTOR(mins, 1, ITEMFL_CHANGED_SIZE) @@ -388,8 +399,6 @@ NSItem::EvaluateEntity(void) EVALUATE_VECTOR(avelocity, 0, ITEMFL_CHANGED_ANGULARVELOCITY) EVALUATE_VECTOR(avelocity, 1, ITEMFL_CHANGED_ANGULARVELOCITY) EVALUATE_VECTOR(avelocity, 2, ITEMFL_CHANGED_ANGULARVELOCITY) - EVALUATE_FIELD(chain, ITEMFL_CHANGED_CHAIN) - EVALUATE_FIELD(owner, ITEMFL_CHANGED_CHAIN) } #endif @@ -397,17 +406,20 @@ NSItem::EvaluateEntity(void) void NSItem::ReceiveEntity(float flNew, float flChanged) { + READENTITY_ENTNUM(chain_entnum, ITEMFL_CHANGED_CHAIN) + READENTITY_ENTNUM(owner_entnum, ITEMFL_CHANGED_CHAIN) READENTITY_COORD(origin[0], ITEMFL_CHANGED_ORIGIN_X) READENTITY_COORD(origin[1], ITEMFL_CHANGED_ORIGIN_Y) READENTITY_COORD(origin[2], ITEMFL_CHANGED_ORIGIN_Z) READENTITY_ANGLE(angles[0], ITEMFL_CHANGED_ANGLES_X) READENTITY_ANGLE(angles[1], ITEMFL_CHANGED_ANGLES_Y) READENTITY_ANGLE(angles[2], ITEMFL_CHANGED_ANGLES_Z) - READENTITY_SHORT(entityDefID, ITEMFL_CHANGED_MODELINDEX) + READENTITY_FLOAT(entityDefID, ITEMFL_CHANGED_MODELINDEX) READENTITY_SHORT(modelindex, ITEMFL_CHANGED_MODELINDEX) READENTITY_BYTE(solid, ITEMFL_CHANGED_SOLID) READENTITY_BYTE(movetype, ITEMFL_CHANGED_FLAGS) READENTITY_INT(flags, ITEMFL_CHANGED_FLAGS) + READENTITY_INT(modelflags, ITEMFL_CHANGED_FLAGS) READENTITY_INT(vv_flags, ITEMFL_CHANGED_FLAGS) READENTITY_COORD(mins[0], ITEMFL_CHANGED_SIZE) READENTITY_COORD(mins[1], ITEMFL_CHANGED_SIZE) @@ -425,24 +437,25 @@ NSItem::ReceiveEntity(float flNew, float flChanged) READENTITY_COORD(avelocity[0], ITEMFL_CHANGED_ANGULARVELOCITY) READENTITY_COORD(avelocity[1], ITEMFL_CHANGED_ANGULARVELOCITY) READENTITY_COORD(avelocity[2], ITEMFL_CHANGED_ANGULARVELOCITY) - READENTITY_ENTNUM(chain_entnum, ITEMFL_CHANGED_CHAIN) - READENTITY_ENTNUM(owner_entnum, ITEMFL_CHANGED_CHAIN) if (flChanged & ITEMFL_CHANGED_MODELINDEX) { - classname = EntityDef_NameFromID(entityDefID); + classname = EntityDef_NameFromNetID(entityDefID); + declclass = classname; } drawmask = (modelindex != 0) ? MASK_ENGINE : 0; if (flChanged & ITEMFL_CHANGED_CHAIN) { - chain = __NULL__; + m_nextItem = __NULL__; } - if (scale == 0.0f) + if (scale == 0.0f) { scale = 1.0f; + } - if (flChanged & ITEMFL_CHANGED_SIZE) - setsize(this, mins, maxs); + if (flChanged & ITEMFL_CHANGED_SIZE || flChanged & ITEMFL_CHANGED_ORIGIN_X || flChanged & ITEMFL_CHANGED_ORIGIN_Y || flChanged & ITEMFL_CHANGED_ORIGIN_Z) { + Relink(); + } } void @@ -453,14 +466,18 @@ NSItem::ReceiveEvent(float eventType) void NSItem::PredictPreFrame(void) { + if (wasfreed(this)) { + return; + } + /* relink */ - if (chain == __NULL__ && chain_entnum) - chain = findentity(world, ::entnum, chain_entnum); + if (m_nextItem == __NULL__ && chain_entnum) + m_nextItem = findentity(world, ::entnum, chain_entnum); if (owner == __NULL__ && owner_entnum) { owner = findentity(world, ::entnum, owner_entnum); - if (owner) { + if (owner && owner == pSeat->m_ePlayer) { _AddedCallback(); } } @@ -477,7 +494,7 @@ NSItem::PredictPreFrame(void) SAVE_STATE(velocity) SAVE_STATE(avelocity) SAVE_STATE(owner) - SAVE_STATE(chain) + SAVE_STATE(m_nextItem) } void @@ -495,7 +512,7 @@ NSItem::PredictPostFrame(void) ROLL_BACK(velocity) ROLL_BACK(avelocity) ROLL_BACK(owner) - ROLL_BACK(chain) + ROLL_BACK(m_nextItem) } #endif @@ -507,9 +524,12 @@ NSItem::AddedToInventory(void) void NSItem::RemovedFromInventory(void) { -#ifdef SERVER - //BecomePickup(); -#endif +} + +bool +NSItem::InInventory(void) +{ + return (owner) ? (true) : (false); } bool @@ -522,36 +542,38 @@ NSItem::IsWeapon(void) void NSItem::_AddedCallback(void) { + NSActor ourOwner = (NSActor)owner; + #ifdef SERVER - NSActor pl = (NSActor)owner; if (m_iGiveHealth > 0i) { - if (pl.GetHealth() < 100) { + if (ourOwner.GetHealth() < 100) { //Damage_Apply(pl, this, -m_iGiveHealth, 0, DMG_GENERIC); - pl.health = bound(0, pl.health + m_iGiveHealth, pl.max_health); + ourOwner.health = bound(0, ourOwner.health + m_iGiveHealth, ourOwner.max_health); } } if (m_iGiveArmor > 0i) { - if (pl.armor < 100) { - pl.armor += (float)m_iGiveArmor; + if (ourOwner.armor < 100) { + ourOwner.armor += (float)m_iGiveArmor; - if (pl.armor > 100) { - pl.armor = 100; + if (ourOwner.armor > 100) { + ourOwner.armor = 100; } } } for (int i = 0; i < MAX_AMMO_TYPES; i++) { - pl.GiveAmmo(i, m_GiveAmmo[i]); + ourOwner.GiveAmmo(i, m_GiveAmmo[i]); } - Logging_Pickup(pl, this, __NULL__); - pl.StartSoundDef(m_sndAcquire, CHAN_ITEM, true); + Logging_Pickup(ourOwner, this, __NULL__); + ourOwner.StartSoundDef(m_sndAcquire, CHAN_ITEM, true); OnPickup(); Disappear(); #endif + ourOwner.AddedItemCallback(this); AddedToInventory(); } @@ -559,4 +581,8 @@ void NSItem::_RemovedCallback(void) { RemovedFromInventory(); + owner = __NULL__; + chain = __NULL__; + m_nextItem = __NULL__; + Destroy(); } diff --git a/src/shared/NSMonster.qc b/src/shared/NSMonster.qc index 7e89e856..32fec875 100644 --- a/src/shared/NSMonster.qc +++ b/src/shared/NSMonster.qc @@ -508,7 +508,7 @@ NSMonster::Gib(void) { vector vecDir = vectorToAngles(GetOrigin() - g_dmg_vecLocation); SetState(MONSTER_DEAD); - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); string breakModel = GetPropData(PROPINFO_BREAKMODEL); BreakModel_Spawn(absmin, absmax, vecDir, g_dmg_iDamage * 2.5f, vlen(size) / 10, breakModel); @@ -1805,7 +1805,8 @@ NSMonster::Respawn(void) RestoreAngles(); v_angle = fixAngle(GetAngles()); AddFlags(FL_MONSTER); - SetTakedamage(DAMAGE_AIM); + MakeVulnerable(); + EnableAimAssist(); SetState(MONSTER_IDLE); SetHealth(GetSpawnFloat("health")); m_eEnemy = __NULL__; diff --git a/src/shared/NSNavAI.h b/src/shared/NSNavAI.h index a7af6e3a..bf9b86b8 100644 --- a/src/shared/NSNavAI.h +++ b/src/shared/NSNavAI.h @@ -124,11 +124,15 @@ public: /** Retrieve the 'last' weapon they had chosen. If not valid, returns the next best. */ nonvirtual NSWeapon GetLastWeapon(void); + virtual void AddedItemCallback(NSItem); + #ifdef SERVER /* overrides */ virtual void Save(float); virtual void Restore(string,string); virtual void RestoreComplete(void); + virtual void Spawned(void); + virtual void Input(entity, string, string); virtual void DebugDraw(void); /* methods we'd like others to override */ @@ -180,12 +184,15 @@ private: /* These are defined in side defs\*.def, ammo_types and ammo_names */ int m_iAmmoTypes[MAX_AMMO_TYPES]; - NSItem m_itemList; - NSWeapon m_activeWeapon; NSWeapon m_activeWeapon_net; float activeweapon; - float m_flFirstInventoryItem; + NETWORKED_FLOAT(m_flFirstInventoryItem) }; +/* for now here to make debugging easier */ +.NSItem m_itemList; +.NSWeapon m_activeWeapon; +.NSWeapon m_firstWeapon; + void NSActor_ListInventory(NSActor); diff --git a/src/shared/NSNavAI.qc b/src/shared/NSNavAI.qc index eadcbc5c..10851148 100644 --- a/src/shared/NSNavAI.qc +++ b/src/shared/NSNavAI.qc @@ -24,10 +24,12 @@ NSActor::NSActor(void) m_vecLastNode = [0,0,0]; m_vecTurnAngle = [0,0,0]; m_flMoveSpeedKey = 0.0f; - + _m_flRouteGiveUp = 0.0f; #endif + m_itemList = __NULL__; + for (int i = 0; i < MAX_AMMO_TYPES; i++) { m_iAmmoTypes[i] = 0; } @@ -142,6 +144,63 @@ NSActor::Restore(string strKey, string strValue) } } +void +NSActor::Spawned(void) +{ + super::Spawned(); + + /* let's not waste time and resources */ + if (wasfreed(this)) { + return; + } + + /* start ammo */ + for (int i = 0; i < MAX_AMMO_TYPES; i++) { + string defKey = ammoNameForNum(i); + + if (defKey != "") { + int defValue = GetDefInt(defKey); + EntLog("Giving %S some %i units of %S", classname, defValue, defKey); + GiveAmmo(i, defValue); + } + } + + /* give them their inventory */ + string weaponList = GetDefString("weapon"); + string itemList = GetDefString("item"); + int desiredWeapon = GetDefInt("current_weapon"); + string switchTo = ""; + int weaponCount = tokenizebyseparator(weaponList, ","); + + /* append items to the end */ + if (itemList != "") { + weaponList = sprintf("%s,%s", weaponList, itemList); + } + + for (int i = 0; i < weaponCount; i++) { + string itemName = argv(i); + GiveItem(itemName); + + if (i == desiredWeapon) { + switchTo = itemName; + } + + /* re-calculate because GiveItem may overwrite argv() */ + weaponCount = tokenizebyseparator(weaponList, ","); + } + + if (switchTo != "") { + SwitchToWeapon(switchTo); + } + + /* give them their desired appearance */ + string spawnModel = GetSpawnString("model"); + + if (spawnModel != "") { + SetModel(GetSpawnString("model")); + } +} + void NSActor::RestoreComplete(void) { @@ -152,6 +211,27 @@ NSActor::RestoreComplete(void) /* re-plot our route */ RouteToPosition(m_vecLastNode); } + +void +NSActor::Input(entity eAct, string strInput, string strData) +{ + switch (strInput) { + case "GiveItem": + GiveItem(strData); + break; + case "GiveAmmo": + tokenize_console(strData); + int ammoNum = ammoNumForName(argv(0)); + int ammoCount = stoi(argv(1)); + + if (ammoNum != -1i) { + GiveAmmo(ammoNum, ammoCount); + } + break; + default: + super::Input(eAct, strInput, strData); + } +} #endif @@ -518,6 +598,7 @@ NSActor::LaunchProjectile(string defName, bool thrown, float timeOfs) throwingStrength = bound(0, (90 - throwDirection[0]) * 5.0f, 1000); + nade.SetWeaponOwner(m_activeWeapon); nade.Launch(GetEyePos(), GetViewAngle(), time - timeOfs, 0.0f, 0.0f); makevectors(throwDirection); nade.SetVelocity((v_forward * throwingStrength) + GetVelocity()); @@ -584,7 +665,13 @@ NSActor::GiveAmmo(int ammoType, int ammoAmount) if (ammoType <= 0i || ammoType >= MAX_AMMO_TYPES) return (false); - maxAmmo = ammoMaxForNum(ammoType); + /* read max ammo from entityDef decl first */ + maxAmmo = GetDefInt(sprintf("max_%s", ammoNameForNum(ammoType))); + + /* default when above is 0 */ + if (!maxAmmo) { + maxAmmo = ammoMaxForNum(ammoType); + } /* already at max-ammo? */ if (m_iAmmoTypes[ammoType] >= maxAmmo) @@ -652,7 +739,7 @@ NSActor::HasExactItem(NSItem itemEntity) return (true); } - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } return (false); @@ -679,12 +766,24 @@ NSActor::HasItem(string itemName) return (true); } - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } return (false); } +void +NSActor::AddedItemCallback(NSItem itemAdded) +{ +#ifdef SERVER + if (!m_activeWeapon) { + if (itemAdded.IsWeapon()) { + SwitchToExactWeapon(itemAdded); + } + } +#endif +} + bool NSActor::GiveItem(string itemName) { @@ -707,11 +806,6 @@ NSActor::GiveItem(string itemName) m_itemList._AddedCallback(); //NSLog("First Item %S", m_itemList.classname); - /* we have no active weapon, and this is our pick */ - if (!m_activeWeapon) { - SwitchToExactWeapon((NSWeapon)m_itemList); - } - return (true); } @@ -727,7 +821,7 @@ NSActor::GiveItem(string itemName) } lastItem = linkedList; - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } /* add it to the back of the chain, so it's part of our inventory. */ @@ -740,7 +834,7 @@ NSActor::GiveItem(string itemName) } newItem.SetOwner(this); - lastItem.chain = newItem; + lastItem.m_nextItem = newItem; linkedList = newItem; newItem._AddedCallback(); } @@ -787,7 +881,7 @@ NSActor::SwitchToBestWeapon(bool ignoreActive) float weight; if (toIgnore == weaponName) { - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; continue; } @@ -799,11 +893,13 @@ NSActor::SwitchToBestWeapon(bool ignoreActive) } } - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } if (bestWeapon) { - SwitchToExactWeapon((NSWeapon)bestWeapon); + if (bestWeapon != m_activeWeapon) { + SwitchToExactWeapon((NSWeapon)bestWeapon); + } } } @@ -837,24 +933,30 @@ NSActor::RemoveItem(string itemName) removeItem = true; frontItem = lastItem; /* the one before the current one */ itemToRemove = linkedList; /* the item to destroy */ + break; } lastItem = linkedList; - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } /* successfully remove the last item */ if (removeItem == true) { /* we had an item in front, bridge across the removed item. */ if (frontItem) { - frontItem.chain = (NSItem)itemToRemove.chain; + frontItem.m_nextItem = (NSItem)itemToRemove.m_nextItem; } else { /* this was the first item. set to chain (can be NULL) */ - m_itemList = (NSItem)itemToRemove.chain; + m_itemList = (NSItem)itemToRemove.m_nextItem; + } + + /* is this our active weapon? */ + if (m_activeWeapon == linkedList) { + m_activeWeapon._SwitchedFromCallback(); + m_activeWeapon = __NULL__; } itemToRemove._RemovedCallback(); - //itemToRemove.Destroy(); return (true); } #endif @@ -877,19 +979,18 @@ NSActor::RemoveAllItems(bool ignoreWeapons) /* iterate through the inventory, then figure out if we already have it*/ while (linkedList) { - NSItem nextItem = (NSItem)linkedList.chain; + NSItem nextItem = (NSItem)linkedList.m_nextItem; /* respect weapon filter */ if (!(ignoreWeapons && linkedList.IsWeapon() == true)) { - linkedList._RemovedCallback(); - linkedList.Destroy(); - - /* is this our active weapon? */ + /* is this our active weapon? give it a chance to do work first */ if (m_activeWeapon == linkedList) { m_activeWeapon._SwitchedFromCallback(); m_activeWeapon = __NULL__; } + RemoveItem(linkedList.classname); + linkedList = nextItem; continue; } @@ -914,19 +1015,16 @@ NSActor::RemoveAllWeapons(void) /* iterate through the inventory, then figure out if we already have it*/ while (linkedList) { - NSItem nextItem = (NSItem)linkedList.chain; + NSItem nextItem = (NSItem)linkedList.m_nextItem; - /* respect weapon filter */ if (linkedList.IsWeapon() == true) { - linkedList._RemovedCallback(); - linkedList.Destroy(); - - /* is this our active weapon? */ + /* is this our active weapon? give it a chance to do work first */ if (m_activeWeapon == linkedList) { m_activeWeapon._SwitchedFromCallback(); m_activeWeapon = __NULL__; } + linkedList._RemovedCallback(); continue; } @@ -966,7 +1064,7 @@ NSActor::SwitchToWeapon(string weaponName) SwitchToExactWeapon((NSWeapon)linkedList); } - linkedList = (NSItem)linkedList.chain; + linkedList = (NSItem)linkedList.m_nextItem; } } @@ -1005,9 +1103,18 @@ NSActor::SortWeaponChain(void) /* first we determine the range of our hud buckets. */ while (itemChain) { + + /* no longer in inventory */ + if (itemChain.owner != this) { + itemChain = (NSWeapon)itemChain.m_nextItem; + continue; + } + if (itemChain.IsWeapon() == true) { hudSlot = itemChain.GetDefInt("hudSlot"); hudPos = itemChain.GetDefInt("hudSlotPos"); + itemChain.m_nextWeapon = __NULL__; + itemChain.m_prevWeapon = __NULL__; if (hudSlot > heighestSlot) { heighestSlot = hudSlot; @@ -1017,7 +1124,7 @@ NSActor::SortWeaponChain(void) } } - itemChain = (NSWeapon)itemChain.chain; + itemChain = (NSWeapon)itemChain.m_nextItem; } for (int hS = 0i; hS <= heighestSlot; hS++) { @@ -1025,6 +1132,12 @@ NSActor::SortWeaponChain(void) itemChain = (NSWeapon)m_itemList; while (itemChain) { + /* no longer in inventory */ + if (itemChain.owner != this) { + itemChain = (NSWeapon)itemChain.m_nextItem; + continue; + } + if (itemChain.IsWeapon() == true) { hudSlot = itemChain.GetDefInt("hudSlot"); hudPos = itemChain.GetDefInt("hudSlotPos"); @@ -1043,7 +1156,7 @@ NSActor::SortWeaponChain(void) } } - itemChain = (NSWeapon)itemChain.chain; + itemChain = (NSWeapon)itemChain.m_nextItem; } } } @@ -1054,7 +1167,10 @@ NSActor::SortWeaponChain(void) weaponTest = weaponTest.m_nextWeapon; } - firstWeapon.m_prevWeapon = lastWeapon; + if (firstWeapon) { + firstWeapon.m_prevWeapon = lastWeapon; + m_firstWeapon = firstWeapon; + } return (firstWeapon); } @@ -1062,8 +1178,9 @@ NSActor::SortWeaponChain(void) bool NSWeapon_CanSwitch(NSActor pl) { - if (!pl.m_activeWeapon) + if (!pl.m_activeWeapon) { return false; + } return true; } @@ -1071,36 +1188,28 @@ NSWeapon_CanSwitch(NSActor pl) NSWeapon NSActor::GetNextWeapon(void) { - NSWeapon firstWeapon; - if (NSWeapon_CanSwitch(this) == false) { return (__NULL__); } - firstWeapon = SortWeaponChain(); - if (m_activeWeapon.m_nextWeapon) { return (m_activeWeapon.m_nextWeapon); } else { - return (firstWeapon); + return (m_firstWeapon); } } NSWeapon NSActor::GetPreviousWeapon(void) { - NSWeapon firstWeapon; - if (NSWeapon_CanSwitch(this) == false) { return (__NULL__); } - firstWeapon = SortWeaponChain(); - if (m_activeWeapon.m_prevWeapon) { return (m_activeWeapon.m_prevWeapon); } else { - return (firstWeapon); + return (m_firstWeapon); } } @@ -1121,8 +1230,8 @@ NSActor_ListInventory(NSActor targetEntity) printf("active weapon: %s (%d)\n", activeWeapon.classname, num_for_edict(activeWeapon)); while (itemEntry) { - printf("%i %s (%d) (owner: %d); next: %d\n", i, itemEntry.classname, num_for_edict(itemEntry), num_for_edict(itemEntry.owner), num_for_edict(itemEntry.chain)); - itemEntry = (NSItem)itemEntry.chain; + printf("%i %s (%d) (owner: %d); next: %d\n", i, itemEntry.classname, num_for_edict(itemEntry), num_for_edict(itemEntry.owner), num_for_edict(itemEntry.m_nextItem)); + itemEntry = (NSItem)itemEntry.m_nextItem; i += 1i; } } diff --git a/src/shared/NSPhysicsEntity.qc b/src/shared/NSPhysicsEntity.qc index 542281a5..680f3fb2 100644 --- a/src/shared/NSPhysicsEntity.qc +++ b/src/shared/NSPhysicsEntity.qc @@ -455,7 +455,7 @@ NSPhysicsEntity::_TouchThink(void) } } -#if 0 +#if 1 if (m_flCheckTime < time) { bool should if (vlen(m_vecPrevAngles - angles) < 1) { @@ -571,7 +571,7 @@ NSPhysicsEntity::Respawn(void) SetSolid(SOLID_BSP); #ifdef SERVER - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); #endif Sleep(); diff --git a/src/shared/NSProjectile.h b/src/shared/NSProjectile.h index 948de82e..8125c495 100644 --- a/src/shared/NSProjectile.h +++ b/src/shared/NSProjectile.h @@ -87,6 +87,9 @@ public: nonvirtual void SetLightColor(vector); nonvirtual void SetLightRadius(float); + nonvirtual void SetWeaponOwner(NSWeapon); + nonvirtual NSWeapon GetWeaponOwner(void); + nonvirtual void EnableDetonateOnFuse(bool); nonvirtual void EnableDetonateOnDeath(bool); nonvirtual void EnableDetonateOnWorld(bool); @@ -154,6 +157,7 @@ private: vector m_vecSpawnMaxs; float m_flSpawnFrame; vector m_vecSpawnOrigin; + NSWeapon m_weaponOwner; /* ETQW-additions */ bool m_bIsBullet; @@ -186,7 +190,7 @@ private: }; #ifdef SERVER -NSProjectile NSProjectile_SpawnDef(string entityDef, NSEntity theOwner); -NSProjectile NSProjectile_SpawnDefAtPosition(string entityDef, NSEntity theOwner, vector vecOrigin, vector vecAngles); -NSProjectile NSProjectile_SpawnDefAttachment(string entityDef, NSEntity theOwner, int attachmentID); +NSProjectile NSProjectile_SpawnDef(string entityDef, NSActor theOwner); +NSProjectile NSProjectile_SpawnDefAtPosition(string entityDef, NSActor theOwner, vector vecOrigin, vector vecAngles); +NSProjectile NSProjectile_SpawnDefAttachment(string entityDef, NSActor theOwner, int attachmentID); #endif diff --git a/src/shared/NSProjectile.qc b/src/shared/NSProjectile.qc index 585398f8..ab5cc22d 100644 --- a/src/shared/NSProjectile.qc +++ b/src/shared/NSProjectile.qc @@ -508,7 +508,7 @@ NSProjectile::Touch(entity eToucher) NSSurfacePropEntity toucher = (NSSurfacePropEntity)eToucher; NSDict damageDecl = spawn(NSDict); damageDecl.AddKey("damage", GetSubDefString(m_defDamage, "damage")); - toucher.Damage(this, owner, damageDecl, m_flDmgMultiplier, vectorNormalize(angles), trace_endpos); + toucher.Damage(this, owner, damageDecl, m_flDmgMultiplier, vectorNormalize(angles), m_vecImpactPos); remove(damageDecl); } @@ -567,6 +567,18 @@ NSProjectile::SetLightRadius(float newRadius) m_flLightRadius = newRadius; } +void +NSProjectile::SetWeaponOwner(NSWeapon weaponOwner) +{ + m_weaponOwner = weaponOwner; +} + +NSWeapon +NSProjectile::GetWeaponOwner(void) +{ + return (m_weaponOwner); +} + void NSProjectile::EnableDetonateOnFuse(bool enabled) { @@ -724,12 +736,13 @@ NSProjectile::_Explode(entity explodedOn) float flDamage = GetSubDefFloat(m_defSplashDamage, "damage"); float flRadius = GetSubDefFloat(m_defSplashDamage, "radius"); - if (m_flDmgMultiplier >= 0.0) + if (m_flDmgMultiplier >= 0.0) { flDamage *= m_flDmgMultiplier; + } //print(sprintf("Damage: %d; Radius: %d\n", flDamage, flRadius)); //Damage_Radius(origin, owner, flDamage, flRadius, TRUE, 0); - radiusDamage(origin, flRadius, 0i, (int)flDamage, owner); + radiusDamage(GetOrigin(), flRadius, 0i, (int)flDamage, owner); } /* another def that'll be spawned when this one detonates */ @@ -796,10 +809,11 @@ NSProjectile::_ApplyDamage(void) trace_surface_id = m_iMultiBody; g_dmg_vecLocation = trace_endpos; NSDict damageDecl = spawn(NSDict); + damageDecl.AddKey("hitbody", itos(trace_surface_id)); damageDecl.AddKey("damage", itos(m_iMultiValue)); vector dmgDir = dirFromTarget(GetOrigin(), m_eMultiTarget.origin); - entityDamage(m_eMultiTarget, owner, owner, damageDecl.GetDeclBody(), classname, GetOrigin(), dmgDir, trace_endpos); + entityDamage(m_eMultiTarget, owner, owner, damageDecl.GetDeclBody(), GetWeaponOwner().classname, GetOrigin(), dmgDir, trace_endpos); remove(damageDecl); m_eMultiTarget = __NULL__; @@ -859,41 +873,13 @@ NSProjectile::_FireSingle(vector vecPos, vector vecAngles, float flDamage, float if (trace_ent.takedamage != DAMAGE_NO && trace_ent.iBleeds) { Sound_Play(trace_ent, CHAN_BODY, "damage_bullet.hit"); -#ifdef CSTRIKE - NSClientPlayer pl = (NSClientPlayer)trace_ent; - /* modify the damage based on the location */ - switch (trace_surface_id) { - case BODY_HEAD: - /* the helmet is one power house */ - if (pl.g_items & ITEM_HELMET) { - flDamage = 0; - Sound_Play(trace_ent, CHAN_BODY, "player.headshotarmor"); - pl.g_items &= ~ITEM_HELMET; - return; - } else { - flDamage *= 4; - Sound_Play(trace_ent, CHAN_BODY, "player.headshot"); - } - break; - case BODY_STOMACH: - flDamage *= 0.9; - if (pl.armor > 0) - Sound_Play(trace_ent, CHAN_BODY, "player.hitarmor"); - break; - case BODY_LEGLEFT: - case BODY_LEGRIGHT: - flDamage *= 0.4; - break; - } -#else /* only headshots count in HL */ if (trace_surface_id == BODY_HEAD) flDamage *= 3; -#endif } #ifdef WASTES - TWPlayer pl1 = (TWPlayer)self; + TWPlayer pl1 = (TWPlayer)owner; if (pl1.m_iWillpowerValue > 0) { FX_Crit(endPos, vectoangles(endPos - pl1.origin), 0); } @@ -1024,9 +1010,9 @@ NSProjectile::Launch(vector startPos, vector launchDir, float fuseOffset, float } if (GetHealth() > 0) { - SetTakedamage(DAMAGE_YES); + MakeVulnerable(); } else { - SetTakedamage(DAMAGE_NO); + MakeInvulnerable(); } if (m_flFuse > 0) { @@ -1151,7 +1137,7 @@ NSProjectile::predraw(void) #ifdef SERVER NSProjectile -NSProjectile_SpawnDef(string entityDef, NSEntity theOwner) +NSProjectile_SpawnDef(string entityDef, NSActor theOwner) { entity oldself = self; @@ -1160,13 +1146,13 @@ NSProjectile_SpawnDef(string entityDef, NSEntity theOwner) self = rocket; EntityDef_SpawnClassname(entityDef); self = oldself; - + rocket.SetWeaponOwner(theOwner.m_activeWeapon); rocket.Launch(theOwner.GetEyePos(), theOwner.GetViewAngle(), 0.0f, 0.0f, 0.0f); return rocket; } NSProjectile -NSProjectile_SpawnDefAtPosition(string entityDef, NSEntity theOwner, vector vecOrigin, vector vecAngles) +NSProjectile_SpawnDefAtPosition(string entityDef, NSActor theOwner, vector vecOrigin, vector vecAngles) { entity oldself = self; NSProjectile rocket = spawn(NSProjectile); @@ -1174,12 +1160,13 @@ NSProjectile_SpawnDefAtPosition(string entityDef, NSEntity theOwner, vector vecO self = rocket; EntityDef_SpawnClassname(entityDef); self = oldself; + rocket.SetWeaponOwner(theOwner.m_activeWeapon); rocket.Launch(vecOrigin, vecAngles, 0.0f, 0.0f, 0.0f); return rocket; } NSProjectile -NSProjectile_SpawnDefAttachment(string entityDef, NSEntity theOwner, int attachmentID) +NSProjectile_SpawnDefAttachment(string entityDef, NSActor theOwner, int attachmentID) { entity oldself = self; float skeletonIndex = skel_create(theOwner.modelindex); @@ -1191,6 +1178,7 @@ NSProjectile_SpawnDefAttachment(string entityDef, NSEntity theOwner, int attachm self = rocket; EntityDef_SpawnClassname(entityDef); self = oldself; + rocket.SetWeaponOwner(theOwner.m_activeWeapon); rocket.Launch(attachmentPos, theOwner.GetViewAngle(), 0.0f, 0.0f, 0.0f); return rocket; } diff --git a/src/shared/NSRenderableEntity.qc b/src/shared/NSRenderableEntity.qc index 1d4f7891..dbe54bf4 100644 --- a/src/shared/NSRenderableEntity.qc +++ b/src/shared/NSRenderableEntity.qc @@ -67,6 +67,10 @@ void NSRenderableEntity::_UpdateGeomset(void) void NSRenderableEntity::_UpdateBoneCount(void) { + if (!modelindex) { + return; + } + skeletonindex = skel_create(modelindex); m_iNumBones = skel_get_numbones(skeletonindex) + 1; //print(sprintf("UPDATED GEOMSET FOR MODELINDEX %d, %d BONES\n", modelindex, m_iNumBones)); @@ -163,10 +167,7 @@ NSRenderableEntity::EvaluateEntity(void) float NSRenderableEntity::SendEntity(entity ePEnt, float flChanged) { - if (!modelindex) - return (0); - - if (clienttype(ePEnt) != CLIENTTYPE_REAL) + if (!modelindex && solid == SOLID_NOT) return (0); WriteByte(MSG_ENTITY, ENT_ENTITYRENDERABLE); @@ -215,7 +216,7 @@ NSRenderableEntity::SendEntity(entity ePEnt, float flChanged) SENDENTITY_COORD(maxs[0], RDENT_CHANGED_SIZE) SENDENTITY_COORD(maxs[1], RDENT_CHANGED_SIZE) SENDENTITY_COORD(maxs[2], RDENT_CHANGED_SIZE) - SENDENTITY_BYTE(frame, RDENT_CHANGED_FRAME) + SENDENTITY_FLOAT(frame, RDENT_CHANGED_FRAME) SENDENTITY_FLOAT(frame1time, RDENT_CHANGED_FRAME) SENDENTITY_FLOAT(skin, RDENT_CHANGED_SKIN) SENDENTITY_FLOAT(effects, RDENT_CHANGED_EFFECTS) @@ -275,7 +276,7 @@ NSRenderableEntity::ReceiveEntity(float flNew, float flChanged) READENTITY_COORD(maxs[0], RDENT_CHANGED_SIZE) READENTITY_COORD(maxs[1], RDENT_CHANGED_SIZE) READENTITY_COORD(maxs[2], RDENT_CHANGED_SIZE) - READENTITY_BYTE(frame, RDENT_CHANGED_FRAME) + READENTITY_FLOAT(frame, RDENT_CHANGED_FRAME) READENTITY_FLOAT(frame1time, RDENT_CHANGED_FRAME) READENTITY_FLOAT(skin, RDENT_CHANGED_SKIN) READENTITY_FLOAT(effects, RDENT_CHANGED_EFFECTS) @@ -1026,7 +1027,7 @@ NSRenderableEntity::HandleAnimEvent(float flTimeStamp, int iCode, string strData tokenize(m_strModelEventCB); /* ensure argv() is 'rewound'... */ } } - + //print(sprintf("Received: %f %i %S\n", flTimeStamp, iCode, strData)); EntWarning("Unknown server model event: %f %i %S", flTimeStamp, iCode, strData); #else diff --git a/src/shared/NSSoundScape.qc b/src/shared/NSSoundScape.qc index 940abdd0..03966ca6 100644 --- a/src/shared/NSSoundScape.qc +++ b/src/shared/NSSoundScape.qc @@ -846,7 +846,7 @@ EFX_UpdateSoundScape(NSSoundScape scape) scape.SetupEFXReverb(&reverbInfo); setup_reverb(12, &reverbInfo, sizeof(reverbinfo_t)); - NSError("Updated sound scape (expensive) %f", reverbInfo.flDensity); + //NSError("Updated sound scape (expensive) %f", reverbInfo.flDensity); } void @@ -906,7 +906,7 @@ EFX_UpdateListener(NSView playerView) return; } - NSError("Soundscape changed to %v", bestScape.GetOrigin()); + //NSError("Soundscape changed to %v", bestScape.GetOrigin()); playerView.SetSoundScape(bestScape); EFX_UpdateSoundScape(bestScape); lastScape = bestScape; diff --git a/src/shared/NSSurfacePropEntity.h b/src/shared/NSSurfacePropEntity.h index a46859e1..f3b88b5e 100644 --- a/src/shared/NSSurfacePropEntity.h +++ b/src/shared/NSSurfacePropEntity.h @@ -114,8 +114,6 @@ public: nonvirtual void MakeVulnerable(void); /** Makes the entity invulnerable if it wasn't already. */ nonvirtual void MakeInvulnerable(void); - /** Deprecated: Sets whether the entity can take damage */ - nonvirtual void SetTakedamage(float); /** Sets the current health of the entity. */ nonvirtual void SetHealth(float); /** Sets the maximum amount of health the entity can have */ diff --git a/src/shared/NSSurfacePropEntity.qc b/src/shared/NSSurfacePropEntity.qc index 489834ac..9c66a9a6 100644 --- a/src/shared/NSSurfacePropEntity.qc +++ b/src/shared/NSSurfacePropEntity.qc @@ -77,9 +77,9 @@ NSSurfacePropEntity::IsAlive(void) } bool -NSSurfacePropEntity:: IsVulnerable(void) +NSSurfacePropEntity::IsVulnerable(void) { - return m_bTakesDamage; + return (m_bTakesDamage); } void @@ -142,12 +142,6 @@ NSSurfacePropEntity::CanBleed(void) return (iBleeds); } -void -NSSurfacePropEntity::SetTakedamage(float type) -{ - takedamage = type; -} - void NSSurfacePropEntity::SetHealth(float new_health) { @@ -437,6 +431,13 @@ NSSurfacePropEntity::Input(entity entityActivator, string inputName, string data case "Extinguish": Extinguish(); break; + case "Damage": + NSDict damageDecl = spawn(NSDict); + float damagePoints = stof(dataField); + damageDecl.AddKey("damage", dataField); + Damage(this, this, damageDecl, 1.0, g_vec_null, GetOrigin()); + remove(damageDecl); + break; default: super::Input(entityActivator, inputName, dataField); } @@ -831,6 +832,7 @@ entityDamage(entity targetEnt, entity inflictingEnt, entity attackingEnt, string } NSDict damageDecl = NSDict::InitWithSpawnData(damageDef); + damageDecl.AddKey("weapon", weaponDef); theTarget.Damage(inflictingEnt, attackingEnt, damageDecl, 1.0, damageDir, hitLocation); remove(damageDecl); #endif diff --git a/src/shared/NSTrigger.qc b/src/shared/NSTrigger.qc index 9e12dda1..7ee25365 100644 --- a/src/shared/NSTrigger.qc +++ b/src/shared/NSTrigger.qc @@ -325,6 +325,9 @@ NSTrigger::Input(entity eAct, string strInput, string strData) case "Toggle": m_bEnabled = (m_bEnabled) ? false : true; break; + case "SetTeam": + SetTeam(stof(strData)); + break; default: super:: Input(eAct, strInput, strData); } @@ -336,7 +339,7 @@ NSTrigger::SetTeam(float new_team) team = new_team; /* update the InfoKey too if it's a client entity */ - if (flags & FL_CLIENT) { + if (isClient(this)) { NSClient client = (NSClient)this; client.SetInfoKey("*team", sprintf("%d", new_team)); } diff --git a/src/shared/NSWeapon.h b/src/shared/NSWeapon.h index d39e0724..838dc9e9 100644 --- a/src/shared/NSWeapon.h +++ b/src/shared/NSWeapon.h @@ -19,9 +19,7 @@ typedef enumflags WEAPONFL_CHANGED_MODELINDEX, WEAPONFL_CHANGED_ORIGIN, WEAPONFL_CHANGED_ANGLES, - WEAPONFL_CHANGED_VELOCITY_X, - WEAPONFL_CHANGED_VELOCITY_Y, - WEAPONFL_CHANGED_VELOCITY_Z, + WEAPONFL_CHANGED_VELOCITY, WEAPONFL_CHANGED_ANGULARVELOCITY, WEAPONFL_CHANGED_SIZE, WEAPONFL_CHANGED_FLAGS, @@ -35,7 +33,9 @@ typedef enumflags WEAPONFL_CHANGED_ENTITYDEF, WEAPONFL_CHANGED_CLIP, WEAPONFL_CHANGED_CHAIN, - WEAPONFL_CHANGED_STATE + WEAPONFL_CHANGED_STATE, + WEAPONFL_CHANGED_NEXTWEAPON, + WEAPONFL_CHANGED_PREVWEAPON, } nsweapon_changed_t; typedef enum @@ -190,6 +190,7 @@ public: virtual void ReceiveEvent(float); #endif + virtual bool IsEmpty(void); virtual bool IsWeapon(void); virtual bool HasReserveAmmo(void); @@ -317,14 +318,17 @@ private: float m_flSpeedMod; bool m_bAltModeSwitch; - NSWeapon m_nextWeapon; - NSWeapon m_prevWeapon; + float m_nextWeapon_entnum; + float m_prevWeapon_entnum; + NSWeapon m_nextWeapon_net; + NSWeapon m_prevWeapon_net; /* cached fireInfo */ string m_fiDetonateOnFire; float m_fiMeleeRange; vector m_fiPunchAngle; string m_fiSndFire; + string m_fiSndFireLast; string m_fiSndRelease; string m_fiSndEmpty; int m_fiAmmoType; @@ -359,6 +363,9 @@ private: NETWORKED_BOOL(m_flOverheating) }; +.NSWeapon m_nextWeapon; +.NSWeapon m_prevWeapon; + /* Helper functions for plugins, the rest of the codebase etc. These are, according to IW, common operations. Makes sense to diff --git a/src/shared/NSWeapon.qc b/src/shared/NSWeapon.qc index 06ab61c0..abace5d5 100644 --- a/src/shared/NSWeapon.qc +++ b/src/shared/NSWeapon.qc @@ -33,7 +33,7 @@ NSWeapon::NSWeapon(void) m_bSmokeContinous = false; m_iClip = 0i; m_iClipSize = 0i; - m_iMode = 0i; + m_iMode = 0; m_viewModel = 0; m_worldModel = 0; m_playerModel = 0.0f; @@ -42,6 +42,8 @@ NSWeapon::NSWeapon(void) m_strLastFireInfo = __NULL__; m_flSpeedMod = 1.0f; m_flReloadSpeed = -1.0f; + m_nextWeapon = __NULL__; + m_prevWeapon = __NULL__; } void @@ -54,9 +56,16 @@ NSWeapon::_AddedCallback(void) return; } + _CacheWeaponDefVariables(); + /* after being stored in someones inventory, re-sort our inventory according to their logic */ ourOwner.SortWeaponChain(); + + /* we have no active weapon, and this is our pick */ + if (!ourOwner.m_activeWeapon) { + ourOwner.SwitchToExactWeapon(this); + } } void @@ -65,7 +74,9 @@ NSWeapon::_RemovedCallback(void) if (owner) { /* give the weapon a chance to clean up the owners inventory */ NSActor ourOwner = (NSActor)owner; + owner = __NULL__; ourOwner.SortWeaponChain(); + owner = ourOwner; } super::_RemovedCallback(); @@ -120,6 +131,7 @@ NSWeapon::UpdateFireInfoCache(void) m_fiMeleeRange = GetSubDefFloat(m_strLastFireInfo, "melee_distance"); m_fiPunchAngle = GetSubDefVector(m_strLastFireInfo, "punchAngle"); m_fiSndFire = GetSubDefString(m_strLastFireInfo, "snd_fire"); + m_fiSndFireLast = GetSubDefString(m_strLastFireInfo, "snd_fireLast"); m_fiSndRelease = GetSubDefString(m_strLastFireInfo, "snd_release"); m_fiSndEmpty = GetSubDefString(m_strLastFireInfo, "snd_empty"); m_fiAmmoType = ammoNumForName(GetSubDefString(m_strLastFireInfo, "ammoType")); @@ -154,14 +166,17 @@ NSWeapon::UpdateFireInfoCache(void) } #ifdef CLIENT - string viewGeomset = GetSubDefString(m_strLastFireInfo, "view_geomset"); - - if (viewGeomset) { - setcustomskin(pSeat->m_eViewModel, "", viewGeomset); - setcustomskin(pSeat->m_eViewModelL, "", viewGeomset); - } else { - setcustomskin(pSeat->m_eViewModel, "", ""); - setcustomskin(pSeat->m_eViewModelL, "", ""); + if (pSeat != __NULL__) { + string viewGeomset = GetSubDefString(m_strLastFireInfo, "view_geomset"); + if (pSeat->m_eViewModel) { + if (viewGeomset) { + setcustomskin(pSeat->m_eViewModel, "", viewGeomset); + setcustomskin(pSeat->m_eViewModelL, "", viewGeomset); + } else { + setcustomskin(pSeat->m_eViewModel, "", ""); + setcustomskin(pSeat->m_eViewModelL, "", ""); + } + } } #endif @@ -173,6 +188,15 @@ NSWeapon::UpdateFireInfoCache(void) NSError("m_flReloadSpeed: %f", m_flReloadSpeed); #endif + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + float zoomFov = GetSubDefFloat(m_strLastFireInfo, "zoomFov"); + + if (zoomFov > 0) { + ourOwner.viewzoom = zoomFov / cvar("fov"); + } else { + ourOwner.viewzoom = 1.0f; + } + /* validate */ if (ammoPerShot != __NULL__) { m_fiAmmoPerShot = (int)stoi(ammoPerShot); @@ -347,61 +371,68 @@ NSWeapon::Restore(string strKey, string strValue) float NSWeapon::SendEntity(entity ePEnt, float flChanged) { + /* item is in somebody elses inventory. */ + if (InInventory() == true && ePEnt != GetOwner()) { + return (false); + } + WriteByte(MSG_ENTITY, ENT_WEAPON); /* broadcast how much data is expected to be read */ WriteFloat(MSG_ENTITY, flChanged); + SENDENTITY_SHORT(modelindex, WEAPONFL_CHANGED_MODELINDEX) - - if (1) { - SENDENTITY_COORD(origin[0], WEAPONFL_CHANGED_ORIGIN) - SENDENTITY_COORD(origin[1], WEAPONFL_CHANGED_ORIGIN) - SENDENTITY_COORD(origin[2], WEAPONFL_CHANGED_ORIGIN) - SENDENTITY_ANGLE(angles[0], WEAPONFL_CHANGED_ANGLES) - SENDENTITY_ANGLE(angles[1], WEAPONFL_CHANGED_ANGLES) - SENDENTITY_ANGLE(angles[2], WEAPONFL_CHANGED_ANGLES) - SENDENTITY_SHORT(entityDefID, WEAPONFL_CHANGED_MODELINDEX) - SENDENTITY_SHORT(m_viewModel, WEAPONFL_CHANGED_MODELINDEX) - SENDENTITY_FLOAT(m_flFireRate, WEAPONFL_CHANGED_MODELINDEX) - SENDENTITY_BYTE(solid, WEAPONFL_CHANGED_SOLID) - SENDENTITY_BYTE(movetype, WEAPONFL_CHANGED_FLAGS) - SENDENTITY_INT(flags, WEAPONFL_CHANGED_FLAGS) - SENDENTITY_INT(vv_flags, WEAPONFL_CHANGED_FLAGS) - SENDENTITY_COORD(mins[0], WEAPONFL_CHANGED_SIZE) - SENDENTITY_COORD(mins[1], WEAPONFL_CHANGED_SIZE) - SENDENTITY_COORD(mins[2], WEAPONFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[0], WEAPONFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[1], WEAPONFL_CHANGED_SIZE) - SENDENTITY_COORD(maxs[2], WEAPONFL_CHANGED_SIZE) - SENDENTITY_BYTE(frame, WEAPONFL_CHANGED_FRAME) - SENDENTITY_FLOAT(skin, WEAPONFL_CHANGED_SKIN) - SENDENTITY_FLOAT(effects, WEAPONFL_CHANGED_EFFECTS) - SENDENTITY_SHORT(m_iBody, WEAPONFL_CHANGED_BODY) - SENDENTITY_FLOAT(scale, WEAPONFL_CHANGED_SCALE) - SENDENTITY_COORD(velocity[0], WEAPONFL_CHANGED_VELOCITY_X) - SENDENTITY_COORD(velocity[1], WEAPONFL_CHANGED_VELOCITY_Y) - SENDENTITY_COORD(velocity[2], WEAPONFL_CHANGED_VELOCITY_Z) - SENDENTITY_COORD(avelocity[0], WEAPONFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_COORD(avelocity[1], WEAPONFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_COORD(avelocity[2], WEAPONFL_CHANGED_ANGULARVELOCITY) - SENDENTITY_BYTE(m_iClip, WEAPONFL_CHANGED_CLIP) - SENDENTITY_BYTE(m_iClipSize, WEAPONFL_CHANGED_CLIP) - SENDENTITY_BYTE(m_iMode, WEAPONFL_CHANGED_CLIP) - SENDENTITY_ENTITY(chain, WEAPONFL_CHANGED_CHAIN) - SENDENTITY_ENTITY(owner, WEAPONFL_CHANGED_CHAIN) - SENDENTITY_BYTE(m_dState, WEAPONFL_CHANGED_STATE) - SENDENTITY_BYTE(m_bFiring, WEAPONFL_CHANGED_STATE) - SENDENTITY_FLOAT(m_flOverheating, WEAPONFL_CHANGED_STATE) - } else { - /* the item is in the inventory */ - } - + SENDENTITY_ENTITY(m_nextItem, WEAPONFL_CHANGED_CHAIN) + SENDENTITY_ENTITY(owner, WEAPONFL_CHANGED_CHAIN) + SENDENTITY_ENTITY(m_nextWeapon, WEAPONFL_CHANGED_NEXTWEAPON) + SENDENTITY_ENTITY(m_prevWeapon, WEAPONFL_CHANGED_PREVWEAPON) + SENDENTITY_COORD(origin[0], WEAPONFL_CHANGED_ORIGIN) + SENDENTITY_COORD(origin[1], WEAPONFL_CHANGED_ORIGIN) + SENDENTITY_COORD(origin[2], WEAPONFL_CHANGED_ORIGIN) + SENDENTITY_ANGLE(angles[0], WEAPONFL_CHANGED_ANGLES) + SENDENTITY_ANGLE(angles[1], WEAPONFL_CHANGED_ANGLES) + SENDENTITY_ANGLE(angles[2], WEAPONFL_CHANGED_ANGLES) + SENDENTITY_FLOAT(entityDefID, WEAPONFL_CHANGED_MODELINDEX) + SENDENTITY_SHORT(m_viewModel, WEAPONFL_CHANGED_MODELINDEX) + SENDENTITY_FLOAT(m_flFireRate, WEAPONFL_CHANGED_MODELINDEX) + SENDENTITY_BYTE(solid, WEAPONFL_CHANGED_SOLID) + SENDENTITY_BYTE(movetype, WEAPONFL_CHANGED_FLAGS) + SENDENTITY_INT(flags, WEAPONFL_CHANGED_FLAGS) + SENDENTITY_INT(modelflags, WEAPONFL_CHANGED_FLAGS) + SENDENTITY_INT(vv_flags, WEAPONFL_CHANGED_FLAGS) + SENDENTITY_COORD(mins[0], WEAPONFL_CHANGED_SIZE) + SENDENTITY_COORD(mins[1], WEAPONFL_CHANGED_SIZE) + SENDENTITY_COORD(mins[2], WEAPONFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[0], WEAPONFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[1], WEAPONFL_CHANGED_SIZE) + SENDENTITY_COORD(maxs[2], WEAPONFL_CHANGED_SIZE) + SENDENTITY_BYTE(frame, WEAPONFL_CHANGED_FRAME) + SENDENTITY_FLOAT(skin, WEAPONFL_CHANGED_SKIN) + SENDENTITY_FLOAT(effects, WEAPONFL_CHANGED_EFFECTS) + SENDENTITY_SHORT(m_iBody, WEAPONFL_CHANGED_BODY) + SENDENTITY_FLOAT(scale, WEAPONFL_CHANGED_SCALE) + SENDENTITY_COORD(velocity[0], WEAPONFL_CHANGED_VELOCITY) + SENDENTITY_COORD(velocity[1], WEAPONFL_CHANGED_VELOCITY) + SENDENTITY_COORD(velocity[2], WEAPONFL_CHANGED_VELOCITY) + SENDENTITY_COORD(avelocity[0], WEAPONFL_CHANGED_ANGULARVELOCITY) + SENDENTITY_COORD(avelocity[1], WEAPONFL_CHANGED_ANGULARVELOCITY) + SENDENTITY_COORD(avelocity[2], WEAPONFL_CHANGED_ANGULARVELOCITY) + SENDENTITY_BYTE(m_iClip, WEAPONFL_CHANGED_CLIP) + SENDENTITY_BYTE(m_iClipSize, WEAPONFL_CHANGED_CLIP) + SENDENTITY_BYTE(m_iMode, WEAPONFL_CHANGED_CLIP) + SENDENTITY_BYTE(m_dState, WEAPONFL_CHANGED_STATE) + SENDENTITY_BYTE(m_bFiring, WEAPONFL_CHANGED_STATE) + SENDENTITY_FLOAT(m_flOverheating, WEAPONFL_CHANGED_STATE) return (1); } void NSWeapon::EvaluateEntity(void) { + EVALUATE_FIELD(m_nextItem, WEAPONFL_CHANGED_CHAIN) + EVALUATE_FIELD(owner, WEAPONFL_CHANGED_CHAIN) + EVALUATE_FIELD(m_nextWeapon, WEAPONFL_CHANGED_NEXTWEAPON) + EVALUATE_FIELD(m_prevWeapon, WEAPONFL_CHANGED_PREVWEAPON) EVALUATE_VECTOR(origin, 0, WEAPONFL_CHANGED_ORIGIN) EVALUATE_VECTOR(origin, 1, WEAPONFL_CHANGED_ORIGIN) EVALUATE_VECTOR(origin, 2, WEAPONFL_CHANGED_ORIGIN) @@ -415,6 +446,7 @@ NSWeapon::EvaluateEntity(void) EVALUATE_FIELD(solid, WEAPONFL_CHANGED_SOLID) EVALUATE_FIELD(movetype, WEAPONFL_CHANGED_FLAGS) EVALUATE_FIELD(flags, WEAPONFL_CHANGED_FLAGS) + EVALUATE_FIELD(modelflags, WEAPONFL_CHANGED_FLAGS) EVALUATE_FIELD(vv_flags, WEAPONFL_CHANGED_FLAGS) EVALUATE_VECTOR(mins, 0, WEAPONFL_CHANGED_SIZE) EVALUATE_VECTOR(mins, 1, WEAPONFL_CHANGED_SIZE) @@ -427,17 +459,15 @@ NSWeapon::EvaluateEntity(void) EVALUATE_FIELD(effects, WEAPONFL_CHANGED_EFFECTS) EVALUATE_FIELD(m_iBody, WEAPONFL_CHANGED_BODY) EVALUATE_FIELD(scale, WEAPONFL_CHANGED_SCALE) - EVALUATE_VECTOR(velocity, 0, WEAPONFL_CHANGED_VELOCITY_X) - EVALUATE_VECTOR(velocity, 1, WEAPONFL_CHANGED_VELOCITY_Y) - EVALUATE_VECTOR(velocity, 2, WEAPONFL_CHANGED_VELOCITY_Z) + EVALUATE_VECTOR(velocity, 0, WEAPONFL_CHANGED_VELOCITY) + EVALUATE_VECTOR(velocity, 1, WEAPONFL_CHANGED_VELOCITY) + EVALUATE_VECTOR(velocity, 2, WEAPONFL_CHANGED_VELOCITY) EVALUATE_VECTOR(avelocity, 0, WEAPONFL_CHANGED_ANGULARVELOCITY) EVALUATE_VECTOR(avelocity, 1, WEAPONFL_CHANGED_ANGULARVELOCITY) EVALUATE_VECTOR(avelocity, 2, WEAPONFL_CHANGED_ANGULARVELOCITY) EVALUATE_FIELD(m_iClip, WEAPONFL_CHANGED_CLIP) EVALUATE_FIELD(m_iClipSize, WEAPONFL_CHANGED_CLIP) EVALUATE_FIELD(m_iMode, WEAPONFL_CHANGED_CLIP) - EVALUATE_FIELD(chain, WEAPONFL_CHANGED_CHAIN) - EVALUATE_FIELD(owner, WEAPONFL_CHANGED_CHAIN) EVALUATE_FIELD(m_dState, WEAPONFL_CHANGED_STATE) EVALUATE_FIELD(m_bFiring, WEAPONFL_CHANGED_STATE) EVALUATE_FIELD(m_flOverheating, WEAPONFL_CHANGED_STATE) @@ -474,50 +504,66 @@ void NSWeapon::ReceiveEntity(float flNew, float flChanged) { READENTITY_SHORT(modelindex, WEAPONFL_CHANGED_MODELINDEX) + READENTITY_ENTNUM(chain_entnum, WEAPONFL_CHANGED_CHAIN) + READENTITY_ENTNUM(owner_entnum, WEAPONFL_CHANGED_CHAIN) + READENTITY_ENTNUM(m_nextWeapon_entnum, WEAPONFL_CHANGED_NEXTWEAPON) + READENTITY_ENTNUM(m_prevWeapon_entnum, WEAPONFL_CHANGED_PREVWEAPON) + + READENTITY_COORD(origin[0], WEAPONFL_CHANGED_ORIGIN) + READENTITY_COORD(origin[1], WEAPONFL_CHANGED_ORIGIN) + READENTITY_COORD(origin[2], WEAPONFL_CHANGED_ORIGIN) + READENTITY_ANGLE(angles[0], WEAPONFL_CHANGED_ANGLES) + READENTITY_ANGLE(angles[1], WEAPONFL_CHANGED_ANGLES) + READENTITY_ANGLE(angles[2], WEAPONFL_CHANGED_ANGLES) + READENTITY_FLOAT(entityDefID, WEAPONFL_CHANGED_MODELINDEX) + READENTITY_SHORT(m_viewModel, WEAPONFL_CHANGED_MODELINDEX) + READENTITY_FLOAT(m_flFireRate, WEAPONFL_CHANGED_MODELINDEX) + READENTITY_BYTE(solid, WEAPONFL_CHANGED_SOLID) + READENTITY_BYTE(movetype, WEAPONFL_CHANGED_FLAGS) + READENTITY_INT(flags, WEAPONFL_CHANGED_FLAGS) + READENTITY_INT(modelflags, WEAPONFL_CHANGED_FLAGS) + READENTITY_INT(vv_flags, WEAPONFL_CHANGED_FLAGS) + READENTITY_COORD(mins[0], WEAPONFL_CHANGED_SIZE) + READENTITY_COORD(mins[1], WEAPONFL_CHANGED_SIZE) + READENTITY_COORD(mins[2], WEAPONFL_CHANGED_SIZE) + READENTITY_COORD(maxs[0], WEAPONFL_CHANGED_SIZE) + READENTITY_COORD(maxs[1], WEAPONFL_CHANGED_SIZE) + READENTITY_COORD(maxs[2], WEAPONFL_CHANGED_SIZE) + READENTITY_BYTE(frame, WEAPONFL_CHANGED_FRAME) + READENTITY_FLOAT(skin, WEAPONFL_CHANGED_SKIN) + READENTITY_FLOAT(effects, WEAPONFL_CHANGED_EFFECTS) + READENTITY_SHORT(m_iBody, WEAPONFL_CHANGED_BODY) + READENTITY_FLOAT(scale, WEAPONFL_CHANGED_SCALE) + READENTITY_COORD(velocity[0], WEAPONFL_CHANGED_VELOCITY) + READENTITY_COORD(velocity[1], WEAPONFL_CHANGED_VELOCITY) + READENTITY_COORD(velocity[2], WEAPONFL_CHANGED_VELOCITY) + READENTITY_COORD(avelocity[0], WEAPONFL_CHANGED_ANGULARVELOCITY) + READENTITY_COORD(avelocity[1], WEAPONFL_CHANGED_ANGULARVELOCITY) + READENTITY_COORD(avelocity[2], WEAPONFL_CHANGED_ANGULARVELOCITY) + READENTITY_BYTE(m_iClip, WEAPONFL_CHANGED_CLIP) + READENTITY_BYTE(m_iClipSize, WEAPONFL_CHANGED_CLIP) + READENTITY_BYTE(m_iMode, WEAPONFL_CHANGED_CLIP) + READENTITY_BYTE(m_dState, WEAPONFL_CHANGED_STATE) + READENTITY_BYTE(m_bFiring, WEAPONFL_CHANGED_STATE) + READENTITY_FLOAT(m_flOverheating, WEAPONFL_CHANGED_STATE) - if (1) { - READENTITY_COORD(origin[0], WEAPONFL_CHANGED_ORIGIN) - READENTITY_COORD(origin[1], WEAPONFL_CHANGED_ORIGIN) - READENTITY_COORD(origin[2], WEAPONFL_CHANGED_ORIGIN) - READENTITY_ANGLE(angles[0], WEAPONFL_CHANGED_ANGLES) - READENTITY_ANGLE(angles[1], WEAPONFL_CHANGED_ANGLES) - READENTITY_ANGLE(angles[2], WEAPONFL_CHANGED_ANGLES) - READENTITY_SHORT(entityDefID, WEAPONFL_CHANGED_MODELINDEX) - READENTITY_SHORT(m_viewModel, WEAPONFL_CHANGED_MODELINDEX) - READENTITY_FLOAT(m_flFireRate, WEAPONFL_CHANGED_MODELINDEX) - READENTITY_BYTE(solid, WEAPONFL_CHANGED_SOLID) - READENTITY_BYTE(movetype, WEAPONFL_CHANGED_FLAGS) - READENTITY_INT(flags, WEAPONFL_CHANGED_FLAGS) - READENTITY_INT(vv_flags, WEAPONFL_CHANGED_FLAGS) - READENTITY_COORD(mins[0], WEAPONFL_CHANGED_SIZE) - READENTITY_COORD(mins[1], WEAPONFL_CHANGED_SIZE) - READENTITY_COORD(mins[2], WEAPONFL_CHANGED_SIZE) - READENTITY_COORD(maxs[0], WEAPONFL_CHANGED_SIZE) - READENTITY_COORD(maxs[1], WEAPONFL_CHANGED_SIZE) - READENTITY_COORD(maxs[2], WEAPONFL_CHANGED_SIZE) - READENTITY_BYTE(frame, WEAPONFL_CHANGED_FRAME) - READENTITY_FLOAT(skin, WEAPONFL_CHANGED_SKIN) - READENTITY_FLOAT(effects, WEAPONFL_CHANGED_EFFECTS) - READENTITY_SHORT(m_iBody, MONFL_CHANGED_BODY) - READENTITY_FLOAT(scale, WEAPONFL_CHANGED_SCALE) - READENTITY_COORD(velocity[0], WEAPONFL_CHANGED_VELOCITY_X) - READENTITY_COORD(velocity[1], WEAPONFL_CHANGED_VELOCITY_Y) - READENTITY_COORD(velocity[2], WEAPONFL_CHANGED_VELOCITY_Z) - READENTITY_COORD(avelocity[0], WEAPONFL_CHANGED_ANGULARVELOCITY) - READENTITY_COORD(avelocity[1], WEAPONFL_CHANGED_ANGULARVELOCITY) - READENTITY_COORD(avelocity[2], WEAPONFL_CHANGED_ANGULARVELOCITY) - READENTITY_BYTE(m_iClip, WEAPONFL_CHANGED_CLIP) - READENTITY_BYTE(m_iClipSize, WEAPONFL_CHANGED_CLIP) - READENTITY_BYTE(m_iMode, WEAPONFL_CHANGED_CLIP) - READENTITY_ENTNUM(chain_entnum, WEAPONFL_CHANGED_CHAIN) - READENTITY_ENTNUM(owner_entnum, WEAPONFL_CHANGED_CHAIN) - READENTITY_BYTE(m_dState, WEAPONFL_CHANGED_STATE) - READENTITY_BYTE(m_bFiring, WEAPONFL_CHANGED_STATE) - READENTITY_FLOAT(m_flOverheating, WEAPONFL_CHANGED_STATE) + /* weapon view model update */ + NSActor ourOwner = (NSActor)findfloat(world, ::entnum, owner_entnum); + + if (ourOwner.m_activeWeapon == this) { + NSRenderableEntity viewModel = (NSRenderableEntity)pSeat->m_eViewModel; + + if (viewModel) { + if (viewModel.modelindex != m_viewModel) { + viewModel.modelindex = m_viewModel; + viewModel._UpdateBoneCount(); + } + } } if (flChanged & WEAPONFL_CHANGED_MODELINDEX) { - classname = EntityDef_NameFromID(entityDefID); + classname = EntityDef_NameFromNetID(entityDefID); + declclass = classname; } if (flChanged & WEAPONFL_CHANGED_BODY) @@ -526,7 +572,15 @@ NSWeapon::ReceiveEntity(float flNew, float flChanged) drawmask = (modelindex != 0) ? MASK_ENGINE : 0; if (flChanged & WEAPONFL_CHANGED_CHAIN) { - chain = __NULL__; + m_nextItem = __NULL__; + } + + if (flChanged & WEAPONFL_CHANGED_NEXTWEAPON) { + m_nextWeapon = __NULL__; + } + + if (flChanged & WEAPONFL_CHANGED_PREVWEAPON) { + m_prevWeapon = __NULL__; } if (scale == 0.0f) @@ -553,11 +607,38 @@ NSWeapon::PredictPreFrame(void) { super::PredictPreFrame(); + if (m_nextWeapon == __NULL__ && m_nextWeapon_entnum) { + m_nextWeapon = (NSWeapon)findentity(world, ::entnum, m_nextWeapon_entnum); + } + + if (m_prevWeapon == __NULL__ && m_prevWeapon_entnum) { + m_prevWeapon = (NSWeapon)findentity(world, ::entnum, m_prevWeapon_entnum); + } + + SAVE_STATE(m_nextWeapon) + SAVE_STATE(m_prevWeapon) + SAVE_STATE(modelindex) + SAVE_STATE(origin) + SAVE_STATE(angles) + SAVE_STATE(entityDefID) + SAVE_STATE(m_viewModel) + SAVE_STATE(m_flFireRate) + SAVE_STATE(solid) + SAVE_STATE(movetype) + SAVE_STATE(flags) + SAVE_STATE(vv_flags) + SAVE_STATE(mins) + SAVE_STATE(maxs) +// SAVE_STATE(frame) +// SAVE_STATE(skin) +// SAVE_STATE(effects) + SAVE_STATE(m_iBody) + SAVE_STATE(scale) + SAVE_STATE(velocity) + SAVE_STATE(avelocity) SAVE_STATE(m_iClip) SAVE_STATE(m_iClipSize) SAVE_STATE(m_iMode) - SAVE_STATE(m_viewModel) - SAVE_STATE(m_flFireRate) SAVE_STATE(m_dState) SAVE_STATE(m_bFiring) SAVE_STATE(m_flOverheating) @@ -568,11 +649,30 @@ NSWeapon::PredictPostFrame(void) { super::PredictPostFrame(); + ROLL_BACK(m_nextWeapon) + ROLL_BACK(m_prevWeapon) + ROLL_BACK(modelindex) + ROLL_BACK(origin) + ROLL_BACK(angles) + ROLL_BACK(entityDefID) + ROLL_BACK(m_viewModel) + ROLL_BACK(m_flFireRate) + ROLL_BACK(solid) + ROLL_BACK(movetype) + ROLL_BACK(flags) + ROLL_BACK(vv_flags) + ROLL_BACK(mins) + ROLL_BACK(maxs) +// ROLL_BACK(frame) +// ROLL_BACK(skin) +// ROLL_BACK(effects) + ROLL_BACK(m_iBody) + ROLL_BACK(scale) + ROLL_BACK(velocity) + ROLL_BACK(avelocity) ROLL_BACK(m_iClip) ROLL_BACK(m_iClipSize) ROLL_BACK(m_iMode) - ROLL_BACK(m_viewModel) - ROLL_BACK(m_flFireRate) ROLL_BACK(m_dState) ROLL_BACK(m_bFiring) ROLL_BACK(m_flOverheating) @@ -599,10 +699,12 @@ NSWeapon::_CacheWeaponDefVariables(void) big in the game state changes. like a save/load. */ m_primaryFireInfo = GetDefString("def_fireInfo"); m_secondaryFireInfo = GetDefString("def_altFireInfo"); + /* MIGRATE INTO FIREINFO */ m_meleeDef = GetDefString("def_melee"); - if (!m_primaryFireInfo) + if (!m_primaryFireInfo) { m_primaryFireInfo = classname; + } firstType = GetSubDefString(m_primaryFireInfo, "ammoType"); @@ -610,7 +712,6 @@ NSWeapon::_CacheWeaponDefVariables(void) ammoRequired = GetSubDefString(m_primaryFireInfo, "ammoRequired"); m_bAmmoRequired = stof(ammoRequired); m_primaryAmmoType = ammoNumForName(firstType); - muzzleModel = GetSubDefString(m_primaryFireInfo, "model_flash"); m_muzzleModelIndex = getmodelindex(muzzleModel, false); m_flPrimedFuse = GetDefFloat("primed_fuse"); @@ -618,10 +719,8 @@ NSWeapon::_CacheWeaponDefVariables(void) m_bPowerAmmo = GetDefBool("powerAmmo"); m_bRemoveOnEmpty = GetDefBool("removeOnEmpty"); m_fxTrail = particleeffectnum(GetDefString("fx_trail")); - m_viewModel = getmodelindex(GetDefString("model_view")); m_worldModel = getmodelindex(GetDefString("model")); - m_bAltModeSwitch = GetDefBool("altMode"); /* gettagindex takes the silliest of parameters to determine which model to query */ @@ -641,7 +740,6 @@ NSWeapon::_CacheWeaponDefVariables(void) secondType = GetSubDefString(m_secondaryFireInfo, "ammoType"); m_secondaryAmmoType = ammoNumForName(secondType); - muzzleModel = GetSubDefString(m_secondaryFireInfo, "model_flash"); m_altMuzzleModelIndex = getmodelindex(muzzleModel, false); } @@ -649,12 +747,14 @@ NSWeapon::_CacheWeaponDefVariables(void) void NSWeapon::_SwitchedToCallback(void) { - _CacheWeaponDefVariables(); SwitchFireInfo(m_primaryFireInfo); SetAttackNext(0.0f); SetIdleNext(0.0f); - Draw(); + +#ifdef CLIENT + PredictPreFrame(); +#endif } void @@ -670,30 +770,78 @@ NSWeapon::IsWeapon(void) return (true); } +bool +NSWeapon::IsEmpty(void) +{ + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + + if (autocvar_g_infiniteAmmo == true) { + return (false); + } + + if (m_fiAmmoRequired) { + /* this weapon uses a clip/magazine */ + if (m_iClipSize && m_fiAmmoType == m_primaryAmmoType) { + /* no more ammo in clip? */ + if (m_iClip <= 0i || (m_iClip - m_fiAmmoPerShot) < 0i) { + /* possible to reload? */ + if (ourOwner.HasAmmo(m_fiAmmoType, 1) == true) { + return (false); + } + return (true); + } + + } else if (ourOwner.HasAmmo(m_fiAmmoType, m_fiAmmoPerShot) == false) { + return (true); + } + } + + return (false); +} + void NSWeapon::Draw(void) { #ifdef CLIENT - NSRenderableEntity viewModel = (NSRenderableEntity)pSeat->m_eViewModel; - viewModel.modelindex = m_viewModel; - viewModel._UpdateBoneCount(); + if (pSeat != __NULL__) { + NSRenderableEntity viewModel = (NSRenderableEntity)pSeat->m_eViewModel; + + if (viewModel && viewModel.classname == "vm") { + viewModel.modelindex = m_viewModel; + if (m_viewModel) { + viewModel._UpdateBoneCount(); + } + } + } #endif - float drawAnimation = 0; - float drawTime; + float drawAnimation = -1; + float drawTime = 0.0f; if (m_iClipSize > 0 && m_iClip == 0) { - drawAnimation = GetSubDefAct(m_strLastFireInfo, "actDrawEmpty"); + drawAnimation = GetSubDefAct(m_primaryFireInfo, "actDrawEmpty"); } - if (!drawAnimation) { - drawAnimation = GetSubDefAct(m_strLastFireInfo, "actDraw"); + /* no empty draw anim exists */ + if (drawAnimation < 0) { + drawAnimation = GetSubDefAct(m_primaryFireInfo, "actDraw"); } - drawTime = frameduration(m_viewModel, drawAnimation); - SetWeaponFrame(drawAnimation); + /* no draw anim exists at all... */ + if (drawAnimation >= 0) { + drawTime = frameduration(m_viewModel, drawAnimation); + SetWeaponFrame(drawAnimation); + } + +#ifdef SERVER + PlaySound(GetDefString("snd_draw"), false); +#endif + SetAttackNext(drawTime); - SetIdleNext(drawTime + 1.0f); + SetIdleNext(drawTime); + + NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); + ourOwner.viewzoom = 1.0f; } void @@ -800,8 +948,15 @@ void NSWeapon::Attack(string fireInfo) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - float shotAnim = 0; + float shotAnim = -1; float fireRate = 1.0f; + + if (GetWeaponState() == WEAPONSTATE_RELOAD) { + _SetWeaponState(WEAPONSTATE_RELOAD_END); + SetIdleNext(0.0f); + Idle(); + return; + } if (m_fiSemiAuto == true) { ourOwner.gflags |= GF_SEMI_TOGGLED; @@ -812,7 +967,12 @@ NSWeapon::Attack(string fireInfo) /* no real attack, detonate named satchels, pipe bombs, etc. */ if (m_fiDetonateOnFire != __NULL__) { if (DetonateDef(m_fiDetonateOnFire) == true) { - SetWeaponFrame(GetSubDefAct(fireInfo, "actDetonate")); + float detonateAct = GetSubDefAct(fireInfo, "actDetonate"); + + if (detonateAct >= 0) { + SetWeaponFrame(GetSubDefAct(fireInfo, "actDetonate")); + } + return; } } @@ -849,16 +1009,18 @@ NSWeapon::Attack(string fireInfo) hitLoc = trace_endpos; ourOwner.hitcontentsmaski = oldhitcontents; + float meleeAnim; if (trace_fraction >= 1.0) { - SetWeaponFrame(GetSubDefAct(fireInfo, "actMeleeMiss")); - meleeRate = GetDefFloat("meleeRateMiss"); + meleeAnim = GetSubDefAct(fireInfo, "actMeleeMiss"); + meleeRate = GetSubDefFloat(fireInfo, "meleeRateMiss"); } else { - SetWeaponFrame(GetSubDefAct(fireInfo, "actMeleeHit")); - meleeRate = GetDefFloat("meleeRateHit"); + meleeAnim = GetSubDefAct(fireInfo, "actMeleeHit"); + meleeRate = GetSubDefFloat(fireInfo, "meleeRateHit"); } + SetWeaponFrame(meleeAnim); SetAttackNext(meleeRate); - SetIdleNext(meleeRate + 1.0f); + SetIdleNext(frameduration(m_viewModel, meleeAnim)); #ifdef SERVER PlaySound(GetSubDefString(m_meleeDef, "snd_miss"), false); @@ -927,10 +1089,17 @@ NSWeapon::Attack(string fireInfo) ourOwner.punchangle += m_fiPunchAngle; #ifdef SERVER + string fireSound = m_fiSndFire; v_angle = input_angles; - if (m_fiSndFire) { - ourOwner.StartSoundDef(m_fiSndFire, CHAN_WEAPON, true); + if (m_iClipSize > 0 && m_iClip == 0) { + if (m_fiSndFireLast != "") { + fireSound = m_fiSndFireLast; + } + } + + if (fireSound) { + ourOwner.StartSoundDef(fireSound, CHAN_WEAPON, true); } #endif @@ -940,8 +1109,8 @@ NSWeapon::Attack(string fireInfo) if (m_iClipSize > 0 && m_iClip == 0) { shotAnim = GetSubDefAct(fireInfo, "actFireLast"); } - - if (!shotAnim) { + + if (shotAnim <= 0) { shotAnim = GetSubDefAct(fireInfo, "actFire"); } @@ -976,6 +1145,7 @@ NSWeapon::Attack(string fireInfo) } else { SetAttackNext(animTime); } + SetIdleNext(animTime); m_bFiring = true; } @@ -990,14 +1160,18 @@ NSWeapon::_PrimaryAttack(void) void NSWeapon::_SecondaryAttack(void) { - _WeaponStartedFiring(); - SecondaryAttack(); + if (GetDefBool("altAlternates") == true) { + return; + } else { + _WeaponStartedFiring(); + SecondaryAttack(); + } } void NSWeapon::_SwitchedWeaponMode(void) { - float animMode; + float animMode = -1; if (m_iMode) { animMode = GetSubDefAct(m_strLastFireInfo, "actModeOn"); @@ -1007,9 +1181,11 @@ NSWeapon::_SwitchedWeaponMode(void) animMode = GetSubDefAct(m_strLastFireInfo, "actModeOff"); } - SetWeaponFrame(animMode); - SetAttackNext(frameduration(m_viewModel, animMode)); - SetIdleNext(frameduration(m_viewModel, animMode)); + if (animMode >= 0) { + SetWeaponFrame(animMode); + SetAttackNext(frameduration(m_viewModel, animMode)); + SetIdleNext(frameduration(m_viewModel, animMode)); + } } void @@ -1032,7 +1208,7 @@ void NSWeapon::Idle(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - float idleAnim = 0; + float idleAnim = -1; m_flOverheating = max(0.0f, m_flOverheating - input_timelength); @@ -1045,7 +1221,10 @@ NSWeapon::Idle(void) case WEAPONSTATE_RELOAD_START: idleAnim = GetSubDefAct(m_strLastFireInfo, "actReloadStart"); _SetWeaponState(WEAPONSTATE_RELOAD); +#ifdef SERVER PlaySound(GetDefString("snd_reload_start"), false); +#endif + SetAttackNext(frameduration(m_viewModel, idleAnim)); break; case WEAPONSTATE_RELOAD: float reloadTime; @@ -1057,18 +1236,17 @@ NSWeapon::Idle(void) _SetWeaponState(WEAPONSTATE_RELOAD_END); } -#ifdef SERVER - PlaySound(GetDefString("snd_reload"), false); -#endif - if (m_flReloadSpeed == -1.0f) { reloadTime = frameduration(m_viewModel, idleAnim); } else { reloadTime = m_flReloadSpeed; } - +#ifdef SERVER + PlaySound(GetDefString("snd_reload"), false); +#endif SetWeaponFrame(idleAnim); SetIdleNext(reloadTime); + /* SetAttackNext(reloadTime); */ /* people like instant feedback, hence commented out */ return; break; case WEAPONSTATE_RELOAD_END: @@ -1077,6 +1255,7 @@ NSWeapon::Idle(void) #ifdef SERVER PlaySound(GetDefString("snd_reload_end"), false); #endif + SetAttackNext(frameduration(m_viewModel, idleAnim)); break; case WEAPONSTATE_FIRELOOP: idleAnim = GetSubDefAct(m_strLastFireInfo, "actLoop"); @@ -1094,19 +1273,29 @@ NSWeapon::Idle(void) _SetWeaponState(WEAPONSTATE_IDLE); SetAttackNext(m_fiOverheatLength); SetReloadNext(0.1f); - break; + /* prevent anything else. */ + return; case WEAPONSTATE_IDLE: default: if (m_iClipSize > 0 && m_iClip == 0) { idleAnim = GetSubDefAct(m_strLastFireInfo, "actIdleEmpty"); - - /* this will mess with us otherwise */ - if (!idleAnim) + + /* TODO: may want this? hard to say. */ + /*if (idleAnim < 0) { + SetIdleNext(5.0f); return; + }*/ } - if (!idleAnim) + if (idleAnim < 0) { idleAnim = GetSubDefAct(m_strLastFireInfo, "actIdle"); + } + } + + /* no anim, so delay it by a bit more so we don't constantly query decl */ + if (idleAnim < 0) { + SetIdleNext(5.0f); + return; } SetWeaponFrame(idleAnim); @@ -1139,6 +1328,10 @@ NSWeapon::PrimaryAttack(void) } } + if (GetDefBool("altAlternates") == true) { + m_iMode = 1 - m_iMode; + } + if (m_iMode) { Attack(m_secondaryFireInfo); return; @@ -1214,9 +1407,11 @@ NSWeapon::_ReloadFinished(void) /* not enough ammo. assign whatever is left. */ if (ourOwner.HasAmmo(m_primaryAmmoType, ammoToDeduct) == false) { ammoToDeduct = ourOwner.m_iAmmoTypes[m_primaryAmmoType]; + m_iClip += ammoToDeduct; + } else { + m_iClip = m_iClipSize; } - m_iClip = m_iClipSize; ourOwner.UseAmmo(m_primaryAmmoType, ammoToDeduct); } #endif @@ -1225,11 +1420,11 @@ void NSWeapon::Reload(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - float reloadAnimation = 0; + float reloadAnimation = -1; float reloadTime; string ammoType; int ammoTypeID; - float reloadStartAct; + float reloadStartAct = -1; /* already in a reload? */ if (m_dState >= WEAPONSTATE_RELOAD_START && m_dState <= WEAPONSTATE_RELOAD_END) { @@ -1247,7 +1442,8 @@ NSWeapon::Reload(void) return; } - ammoType = GetSubDefString(m_strLastFireInfo, "ammoType"); + /* NOTE: this affects our clip, so primary fireInfo only */ + ammoType = GetSubDefString(m_primaryFireInfo, "ammoType"); ammoTypeID = ammoNumForName(ammoType); if (m_iClip == m_iClipSize) { @@ -1261,8 +1457,9 @@ NSWeapon::Reload(void) } /* we have a start-reload, so this is a shotgun styled weapon reload */ - if (reloadStartAct) { + if (reloadStartAct >= 0) { _SetWeaponState(WEAPONSTATE_RELOAD_START); + SetIdleNext(0.0); Release(); return; } @@ -1270,8 +1467,8 @@ NSWeapon::Reload(void) if (m_iClipSize > 0 && m_iClip == 0) { reloadAnimation = GetSubDefAct(m_strLastFireInfo, "actReloadEmpty"); } - - if (!reloadAnimation) { + + if (reloadAnimation < 0) { reloadAnimation = GetSubDefAct(m_strLastFireInfo, "actReload"); } @@ -1281,9 +1478,19 @@ NSWeapon::Reload(void) reloadTime = m_flReloadSpeed; } - PlaySound(GetDefString("snd_reload"), false); - #ifdef SERVER + + string reloadSound = GetDefString("snd_reload"); + + if (m_iClipSize > 0 && m_iClip == 0) { + string emptyReload = GetDefString("snd_reloadEmpty"); + + if (emptyReload != "") { + reloadSound = emptyReload; + } + } + + ourOwner.StartSoundDef(reloadSound, CHAN_AUTO, true); ScheduleThink(_ReloadFinished, reloadTime - 0.1f); #endif @@ -1332,13 +1539,13 @@ NSWeapon::_WeaponStartedFiring(void) } if (m_fiSndFireLoop) { - //ourOwner.StartSoundDef(m_fiSndFireLoop, CHAN_LOOP, true); + ourOwner.StartSoundDef(m_fiSndFireLoop, CHAN_LOOP, true); } //printf("actFireStart %d %S\n", actFireStart, m_strLastFireInfo); #endif - if (actFireStart) { + if (actFireStart >= 0) { SetWeaponFrame(actFireStart); SetAttackNext(frameduration(m_viewModel, actFireStart)); SetIdleNext(frameduration(m_viewModel, actFireStart)); @@ -1363,7 +1570,7 @@ NSWeapon::_WeaponStoppedFiring(void) if (owner.vv_flags & VFL_FIRING) { float actFireStop = GetSubDefAct(m_strLastFireInfo, "actFireStop"); - if (actFireStop) { + if (actFireStop >= 0) { SetWeaponFrame(actFireStop); SetAttackNext(frameduration(m_viewModel, actFireStop)); SetIdleNext(frameduration(m_viewModel, actFireStop)); @@ -1395,7 +1602,7 @@ NSWeapon::WeaponStoppedFiring(void) void NSWeapon::Release(void) { - float idleAnim = 0; + float idleAnim = -1; NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); ourOwner.gflags &= ~GF_SEMI_TOGGLED; @@ -1425,7 +1632,11 @@ NSWeapon::Release(void) ReleasedWeaponAttack(m_strLastFireInfo); m_fiWillRelease = false; - SetWeaponFrame(idleAnim); + + if (idleAnim >= 0) { + SetWeaponFrame(idleAnim); + } + SetAttackNext(1.0f); SetIdleNext(1.0f); ourOwner.vv_flags &= ~VFL_PRIMEDFUSE; @@ -1458,21 +1669,40 @@ NSWeapon::Release(void) #endif m_bFiring = false; - -#if 0 - if (ourOwner.vv_flags & VFL_REDRAW) { - Draw(); - ourOwner.vv_flags &= ~VFL_REDRAW; - return; - } -#endif - Idle(); } void NSWeapon::UpdateGUI(void) { +#ifdef CLIENT + NSClientPlayer ourOwner = __NULL__; + static float baseIconSize = 32.0f; + static float baseIconPadding = 16.0f; + + if (m_bAmmoRequired == false) { + return; + } + + ourOwner = (NSClientPlayer)GetOwner(); + + vector hudSize = g_view.GetHUDCanvasSize(); + vector iconPos = g_view.GetHUDCanvasPos() + (hudSize / 2); + iconPos[0] = (hudSize[0] - baseIconSize) - baseIconPadding; + iconPos[1] = (hudSize[1] - baseIconSize) - baseIconPadding; + drawpic(iconPos, "gfx/hud/armor", [baseIconSize, baseIconSize], [1,1,1], 1.0f); + + ourOwner.a_ammo2 = ourOwner.GetReserveAmmo(m_primaryAmmoType); + iconPos[0] -= baseIconSize + baseIconPadding; + Font_DrawRText(iconPos, itos(ourOwner.a_ammo2), FONT_16); + + iconPos[0] -= baseIconSize + baseIconPadding; + + if (m_iClipSize > 0i) { + ourOwner.a_ammo1 = m_iClip; + Font_DrawRText(iconPos, itos(ourOwner.a_ammo1), FONT_16); + } +#endif } void @@ -1544,14 +1774,15 @@ void NSWeapon::SetIdleNext(float newDelay) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - ourOwner.w_idle_next = newDelay; + /* HACK: RT2 requires this as their anims have junk at the end :/ */ + ourOwner.w_idle_next = bound(0.0, newDelay - 0.05f, newDelay); } bool NSWeapon::CanIdle(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - + if (ourOwner.w_idle_next > 0.0f) { return (false); } @@ -1563,7 +1794,7 @@ bool NSWeapon::CanFire(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - + if (ourOwner.w_attack_next > 0.0f) { return (false); } @@ -1584,7 +1815,7 @@ bool NSWeapon::CanReload(void) { NSClientPlayer ourOwner = (NSClientPlayer)GetOwner(); - + if (ourOwner.w_reload_next > 0.0f) { return (false); } @@ -1694,136 +1925,3 @@ isWeaponDetonationTimed(string weaponDef) return (isFused); } -NSWeapon -NSWeapon_SortWeaponChain(NSActor targetPlayer) -{ - NSWeapon itemChain = (NSWeapon)targetPlayer.m_itemList; - int heighestSlot = -1i; - int heighestPos = -1i; - NSWeapon firstWeapon, lastWeapon; - int hudSlot, hudPos; - - firstWeapon = lastWeapon = __NULL__; - - if (!targetPlayer.m_itemList) { - return __NULL__; - } - - /* first we determine the range of our hud buckets. */ - while (itemChain) { - if (itemChain.IsWeapon() == true) { - hudSlot = itemChain.GetDefInt("hudSlot"); - hudPos = itemChain.GetDefInt("hudSlotPos"); - - if (hudSlot > heighestSlot) { - heighestSlot = hudSlot; - } - if (hudPos > heighestPos) { - heighestPos = hudPos; - } - } - - itemChain = (NSWeapon)itemChain.chain; - } - - for (int hS = 0i; hS <= heighestSlot; hS++) { - for (int hP = 0i; hP <= heighestPos; hP++) { - itemChain = (NSWeapon)targetPlayer.m_itemList; - - while (itemChain) { - if (itemChain.IsWeapon() == true) { - hudSlot = itemChain.GetDefInt("hudSlot"); - hudPos = itemChain.GetDefInt("hudSlotPos"); - - if (hudSlot == hS && hudPos == hP) { - /* first weapon in the chain? */ - if (!lastWeapon) { - firstWeapon = itemChain; - lastWeapon = firstWeapon; - } else { - /* assign this weapon to the last weapon of our chain. */ - lastWeapon.m_nextWeapon = itemChain; - itemChain.m_prevWeapon = lastWeapon; - lastWeapon = itemChain; - } - } - } - - itemChain = (NSWeapon)itemChain.chain; - } - } - } - - /* test */ - NSWeapon weaponTest = firstWeapon; - while (weaponTest) { - weaponTest = weaponTest.m_nextWeapon; - } - - firstWeapon.m_prevWeapon = lastWeapon; - - return (firstWeapon); -} - -/** Select the next item in the list. */ -void -NSWeapon_NextWeapon(NSActor pl) -{ -#ifdef CLIENT - NSWeapon firstWeapon; - - if (NSWeapon_CanSwitch(pl) == false) { - return; - } - - firstWeapon = NSWeapon_SortWeaponChain(pl); - - if (pl.m_activeWeapon.m_nextWeapon) { - NSWeapon_SelectWeapon(pl.m_activeWeapon.m_nextWeapon); - } else { - NSWeapon_SelectWeapon(firstWeapon); - } -#endif -} - -/** Select the previous item in the list. */ -void -NSWeapon_PrevWeapon(NSActor pl) -{ -#ifdef CLIENT - NSWeapon firstWeapon; - - if (NSWeapon_CanSwitch(pl) == false) { - return; - } - - firstWeapon = NSWeapon_SortWeaponChain(pl); - - if (pl.m_activeWeapon.m_prevWeapon) { - NSWeapon_SelectWeapon(pl.m_activeWeapon.m_prevWeapon); - } else { - NSWeapon_SelectWeapon(firstWeapon); - } -#endif -} - -/** Select the previous item in the list. */ -void -NSWeapon_LastWeapon(NSActor pl) -{ -#ifdef CLIENT - NSWeapon firstWeapon; - - if (NSWeapon_CanSwitch(pl) == false) { - return; - } - - firstWeapon = NSWeapon_SortWeaponChain(pl); - - if (pl.m_activeWeapon.m_prevWeapon) { - NSWeapon_SelectWeapon(pl.m_activeWeapon.m_prevWeapon); - } else { - NSWeapon_SelectWeapon(firstWeapon); - } -#endif -} diff --git a/src/shared/NSWeapon_NSNavAI.h b/src/shared/NSWeapon_NSNavAI.h deleted file mode 100644 index e4bf71b2..00000000 --- a/src/shared/NSWeapon_NSNavAI.h +++ /dev/null @@ -1,4 +0,0 @@ - -void NSWeapon_NextWeapon(NSActor); -void NSWeapon_PrevWeapon(NSActor); -void NSWeapon_LastWeapon(NSActor); \ No newline at end of file diff --git a/src/shared/ammo.qc b/src/shared/ammo.qc index ea2941e8..bb53c02f 100644 --- a/src/shared/ammo.qc +++ b/src/shared/ammo.qc @@ -65,7 +65,9 @@ Ammo_Init(void) int ammoIndex = stoi(argv(c+1)); g_ammoInfo[ammoIndex].m_strName = ammoName; g_ammoInfo[ammoIndex].m_strTitle = EntityDef_GetKeyValue("ammo_names", ammoName); - g_ammoInfo[ammoIndex].m_iMax = (int)stof(EntityDef_GetKeyValue("ammo_max", ammoName)); + string maxString = EntityDef_GetKeyValue("ammo_max", ammoName); + string unpackedString = unpackStringCommand(maxString); + g_ammoInfo[ammoIndex].m_iMax = (int)stof(unpackedString); numKeys = tokenize_console(ammoTypeData); } } @@ -78,4 +80,4 @@ Ammo_DebugList(void) printf("%i %S: %S\n", i, g_ammoInfo[i].m_strName, g_ammoInfo[i].m_strTitle); } -} \ No newline at end of file +} diff --git a/src/shared/defs.h b/src/shared/defs.h index 9de3530d..20e3fa41 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -107,7 +107,6 @@ string __fullspawndata; #include "NSItem.h" #include "NSWeapon.h" #include "NSNavAI.h" -#include "NSWeapon_NSNavAI.h" #include "NSMonster.h" #include "NSSquadMonster.h" #include "NSTalkMonster.h" @@ -415,6 +414,23 @@ setorigin_safe(entity target, vector testorg) setorigin(target, testorg); } +#ifdef SERVER +string Skill_GetStringValue(string, string); +#endif + +string +unpackStringCommand(string commandString) +{ +#ifdef SERVER + /* is this supposed to be read from a skill cvar? */ + if (substring(commandString, 0, 6) == "skill:") { + return Skill_GetStringValue(substring(commandString, 6, -1), ""); + } +#endif + + return Constants_LookUp(commandString, commandString); +} + #ifdef NO_LEGACY void readcmd(string foo) @@ -591,7 +607,7 @@ Route_GetJumpVelocity(vector vecFrom, vector vecTo, float flGravMod) } bool -FileExists(string filePath) +fileExists(string filePath) { if (filePath != "") /* not empty */ if not(whichpack(filePath)) /* not present on disk */ @@ -759,4 +775,4 @@ If not available, most new features will be unavailable. * size and volume, unlike [brush-based entities](@ref brushentity). * Generally used for moving, or non moving objects * that interact with the world. - */ \ No newline at end of file + */ diff --git a/src/shared/entities.h b/src/shared/entities.h index 8a960c37..df24f028 100644 --- a/src/shared/entities.h +++ b/src/shared/entities.h @@ -14,6 +14,9 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* used to keep track of decl across different classnames */ +.string declclass; + /** @defgroup entities Entities @brief Objects within the game world, serving various functions. diff --git a/src/shared/entityDef.h b/src/shared/entityDef.h index 1f6bc495..7de60fb8 100644 --- a/src/shared/entityDef.h +++ b/src/shared/entityDef.h @@ -120,14 +120,15 @@ typedef struct void EntityDef_Init(void); void EntityDef_DebugList(void); string EntityDef_GetKeyValue(string, string); - +float EntityDef_NetIDFromName(string); +string EntityDef_NameFromNetID(float); int EntityDef_IDFromName(string); -string EntityDef_NameFromID(int); string EntityDef_GetSpawnData(int); bool EntityDef_HasSpawnClass(string className); #ifdef SERVER bool EntityDef_Precache(string); +NSEntity EntityDef_SwitchClass(NSEntity target, string className); NSEntity Entity_CreateClass(string className); #endif diff --git a/src/shared/entityDef.qc b/src/shared/entityDef.qc index d2080ce4..ef29d364 100644 --- a/src/shared/entityDef.qc +++ b/src/shared/entityDef.qc @@ -195,7 +195,7 @@ EntityDef_Init(void) } /* load the map specific def file */ - if (FileExists(mapDef)) { + if (fileExists(mapDef)) { EntityDef_ReadFile(mapDef); } @@ -220,9 +220,9 @@ EntityDef_Init(void) } static bool -EntityDef_CheckCondition(int id, string keyWord, float tweakCondition, string keyValue) +EntityDef_CheckCondition(NSEntity target, int id, string keyWord, float tweakCondition, string keyValue) { - int spawnWords = tokenize_console(g_lastSpawnData); + int spawnWords = tokenize_console(target.m_strSpawnData); string key, value; float tmp1, tmp2; @@ -273,7 +273,7 @@ EntityDef_CheckCondition(int id, string keyWord, float tweakCondition, string ke } } - return false; + return (false); } @@ -337,6 +337,8 @@ EntityDef_PrepareEntity(entity target, int id) targetEnt = spawn(NSEntity); } + targetEnt.declclass = g_entDefTable[id].entClass; + /* check if the spawnclass is an entityDef */ for (int i = 0i; i < g_entDefCount; i++) { if (g_entDefTable[id].spawnClass == g_entDefTable[i].entClass) { @@ -356,8 +358,8 @@ EntityDef_PrepareEntity(entity target, int id) if (!isfunction(spawnClass)) { NSError("Unable to find entityDef spawnclass %S", spawnClass); - targetEnt.Destroy(); - return __NULL__; + remove(targetEnt); + return (__NULL__); } /* init */ @@ -405,7 +407,7 @@ EntityDef_PrepareEntity(entity target, int id) string keyValue = argv(2); /* iterate through a bunch of different data to check our condition */ - if (EntityDef_CheckCondition(id, keyWord, tweakCondition, keyValue)) { + if (EntityDef_CheckCondition(target, id, keyWord, tweakCondition, keyValue)) { int tweakGroups = tokenizebyseparator(g_entDefTable[id].tweakKeys, ";"); //print(sprintf("%S passed the check\n", keyWord)); @@ -420,30 +422,44 @@ EntityDef_PrepareEntity(entity target, int id) for (int y = 0; y < tweakSpawns; y+= 2) { //print(sprintf("applying %S and %S\n", argv(y), argv(y+1))); targetEnt.SpawnKey(argv(y), argv(y+1)); + targetEnt.m_strSpawnData = sprintf("%s TWEAK %S %S }", targetEnt.m_strSpawnData, argv(y), argv(y+1)); } } /* retokenize */ tweakGroups = tokenizebyseparator(g_entDefTable[id].tweakKeys, ";"); } + + print("\n"); + print("\n"); + print("\n"); + print("\n"); + print(targetEnt.m_strSpawnData); + print("\n"); + print("\n"); + print("\n"); + print("\n"); } /* retokenize our condition */ spawnWords = tokenizebyseparator(g_entDefTable[id].tweakDefs, ";"); } - targetEnt.m_strModelEventCB = g_entDefTable[id].eventList; /* pass over the event listing */ + /* now that that all methods have been called, we're ready to decide whether to continue this spawn */ + if (wasfreed(targetEnt) == false) { + targetEnt.m_strModelEventCB = g_entDefTable[id].eventList; /* pass over the event listing */ - /* now we rename the classname for better visibility, - but also because some classes need to know. */ - targetEnt.classname = g_entDefTable[id].entClass; - targetEnt.entityDefID = EntityDef_IDFromName(targetEnt.classname); + /* now we rename the classname for better visibility, + but also because some classes need to know. */ + targetEnt.classname = g_entDefTable[id].entClass; + targetEnt.entityDefID = EntityDef_NetIDFromName(targetEnt.classname); - targetEnt.Spawned(); - targetEnt.Respawn(); + targetEnt.Spawned(); + targetEnt.Respawn(); + } g_lastSpawnData = ""; - return targetEnt; + return (targetEnt); } /* precache resources inside an entityDef */ @@ -514,6 +530,10 @@ EntityDef_SpawnClassname(string className) { g_lastSpawnData = __fullspawndata; + if not (className) { + return (__NULL__); + } + for (int i = 0i; i < g_entDefCount; i++) { if (className == g_entDefTable[i].entClass) { if (time < 5.0) { @@ -525,7 +545,30 @@ EntityDef_SpawnClassname(string className) } } - return __NULL__; + return (__NULL__); +} + +NSEntity +EntityDef_SwitchClass(NSEntity target, string className) +{ + NSEntity returnEntity = __NULL__; + float oldTeam = target.team; + g_lastSpawnData = target.m_strSpawnData; + + for (int i = 0i; i < g_entDefCount; i++) { + if (className == g_entDefTable[i].entClass) { + if (time < 5.0) { + EntityDef_Precaches(i); + } + + NSLog("Spawning eDef %S", className); + returnEntity = EntityDef_PrepareEntity(target, i); + returnEntity.team = oldTeam; + return (returnEntity); + } + } + + return (returnEntity); } NSEntity @@ -541,10 +584,10 @@ EntityDef_CreateClassname(string className) /* Failure */ if (test == __NULL__) { new.Destroy(); - return __NULL__; + return (__NULL__); } - return new; + return (new); } static void @@ -603,6 +646,23 @@ EntityDef_DebugList(void) } } +float +EntityDef_NetIDFromName(string defName) +{ +#if 1 + return crc16(true, defName); +#else + if (defName) { + for (int i = 0i; i < g_entDefCount; i++) { + if (defName == g_entDefTable[i].entClass) { + return (i); + } + } + } + + return (-1i); +#endif +} int EntityDef_IDFromName(string defName) @@ -619,13 +679,17 @@ EntityDef_IDFromName(string defName) } string -EntityDef_NameFromID(int defNum) +EntityDef_NameFromNetID(float defNum) { - if (defNum == -1i) { - return ""; + for (int i = 0i; i < g_entDefCount; i++) { + int checkSum = crc16(true, g_entDefTable[i].entClass); + + if (checkSum == defNum) { + return (g_entDefTable[i].entClass); + } } - return g_entDefTable[defNum].entClass; + return ""; } diff --git a/src/shared/fteextensions.qc b/src/shared/fteextensions.qc index 7e4d0c82..6c03b4eb 100644 --- a/src/shared/fteextensions.qc +++ b/src/shared/fteextensions.qc @@ -3258,7 +3258,7 @@ void(entity e, string s) clientcommand = #440; /* Part of KRIMZON_SV_PARSECLIENT float(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ string(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ void(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/ -searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE +searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3,SB_MULTISEARCH=1<<4,SB_NAMESORT=1<<5} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. SB_FULLPACKAGEPATH interprets the filterpackage arg as a full package path to avoid gamedir ambiguity, equivelent to whichpack's WP_FULLPACKAGEPATH flag. SB_ALLOWDUPES allows returning multiple entries with the same name (but different package, useful with search_fopen). SB_FORCESEARCH requires use of the filterpackage and SB_FULLPACKAGEPATH flag, initiating searches from gamedirs/packages which are not currently active. */ void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/ diff --git a/src/shared/materials.qc b/src/shared/materials.qc index 816e19eb..835f485d 100644 --- a/src/shared/materials.qc +++ b/src/shared/materials.qc @@ -443,11 +443,11 @@ Materials_Init(void) search_end(pm); /* the way TW did it back in '03 */ - if (FileExists(wastesName)) + if (fileExists(wastesName)) Materials_LoadFromLegacyText(wastesName); /* Trinity-Renderer does it this way */ - if (FileExists(svenName)) + if (fileExists(svenName)) Materials_LoadFromLegacyText(svenName); /* no longer needed! */ diff --git a/src/shared/surfaceproperties.qc b/src/shared/surfaceproperties.qc index a98da965..ab8e5443 100644 --- a/src/shared/surfaceproperties.qc +++ b/src/shared/surfaceproperties.qc @@ -323,6 +323,7 @@ SurfData_Init(void) /* it's OK for one to not exist... */ if (fh < 0) { NSError("missing scripts/surfaceproperties.txt!"); + InitEnd(); return; } @@ -334,6 +335,7 @@ SurfData_Init(void) /* we did not find 'default' as the first entry. */ if (g_spDefaultSet == false) { NSError("no 'default' defined at the top of scripts/surfaceproperties.txt!"); + InitEnd(); return; } diff --git a/src/vgui/ui.qc b/src/vgui/ui.qc index 5e9a5bbf..1d4ee70c 100644 --- a/src/vgui/ui.qc +++ b/src/vgui/ui.qc @@ -447,7 +447,7 @@ UISystem_Init(void) } fclose(fileUI); } else { - error(sprintf("[MENU] Cannot load UI file %s!", strUIFile)); + NSError(sprintf("[MENU] Cannot load UI file %s!", strUIFile)); } #endif