This commit is contained in:
Dominic Szablewski
2021-09-13 09:34:08 +02:00
commit a844ba46f8
95 changed files with 11727 additions and 0 deletions
Executable
+6
View File
@@ -0,0 +1,6 @@
node_modules
package.json
package-lock.json
autosave/
\#*
*~
Executable
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Dominic Szablewski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Executable
+36
View File
@@ -0,0 +1,36 @@
# Q1K3 - A JS13k GAME
My entry for the 2021 [js13k](https://js13kgames.com/) competition.
Play here: https://phoboslab.org/q1k3/
### Controls
- Movement: WASD or Arrow Keys
- Attack: Left Mouse Button
- Jump: Space or Right Mouse Button
- Switch Weapon: Q/E or Mousewheel
### Features
- 2 Levels
- 5 Types of enemies
- 3 Weapons
- 30 different textures
- Music from Andy Lösch: [no-fate](http://no-fate.net/)
- Dynamic lighting
- Doors(!)
- Somewhat robust collision detection, even for fast moving objects
- Enemy AI with line of sight checks. No pathfinding, but still does a reasonable job following the player
- "Spacial" Audio (Stero separation and falloff by distance)
- Maps build with [TrenchBroom](https://trenchbroom.github.io/)
- A map compiler written in C (used during build)
### Libs used
- [Tiny Texture Tumbler](https://github.com/phoboslab/ttt) for texture generation
- A minified fork of [Sonant-X](https://github.com/nicolas-van/sonant-x) for sounds and music
### License
MIT Licensed
Please be aware that this projects makes use of Sonant-X (albeit heavily modified)
which is published under the zlib license.
+3516
View File
File diff suppressed because it is too large Load Diff
+2613
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
# Exported from Wings 3D 2.1.5
mtllib boulder.mtl
o Cube2
#7 vertices, 8 faces
v -1.76671199 -1.11166500 -1.27988643
v -0.66717809 -1.13106144 0.80598548
v -0.75547184 0.39059562 -1.03424222
v 1.71502654 -1.25440865 -1.89713495
v 1.00000000 -1.00000000 1.00000000
v 0.43931125 8.8170497e-2 -1.22055291
v 0.42936450 0.36758775 0.33316664
g Cube2_default
usemtl default
s 1
f 1// 6// 4//
f 2// 3// 1//
f 2// 7// 3//
f 3// 6// 1//
f 3// 7// 6//
f 4// 6// 5//
f 5// 6// 7//
f 5// 7// 2//
+25
View File
@@ -0,0 +1,25 @@
# Exported from Wings 3D 2.1.7
mtllib box.mtl
o Cube1
#8 vertices, 10 faces
v -1.00000000 -1.00000000 -1.00000000
v -1.00000000 -1.00000000 1.00000000
v -1.00000000 1.00000000 -1.00000000
v -1.00000000 1.00000000 1.00000000
v 1.00000000 -1.00000000 -1.00000000
v 1.00000000 -1.00000000 1.00000000
v 1.00000000 1.00000000 -1.00000000
v 1.00000000 1.00000000 1.00000000
g Cube1_default
usemtl default
s 1
f 2// 3// 1//
f 2// 4// 3//
f 2// 8// 4//
f 3// 5// 1//
f 3// 7// 5//
f 3// 8// 7//
f 4// 8// 3//
f 5// 8// 6//
f 6// 8// 2//
f 7// 8// 5//
+33
View File
@@ -0,0 +1,33 @@
# Exported from Wings 3D 2.1.7
mtllib grenade.mtl
o Cylinder1
#10 vertices, 16 faces
v -1.50000000 1.00000000 0.0000000e+0
v -1.50000000 0.30901699 0.95105652
v -1.50000000 -0.80901699 0.58778525
v -1.50000000 -0.80901699 -0.58778525
v -1.50000000 0.30901699 -0.95105652
v 1.50000000 1.00000000 0.0000000e+0
v 1.50000000 0.30901699 0.95105652
v 1.50000000 -0.80901699 0.58778525
v 1.50000000 -0.80901699 -0.58778525
v 1.50000000 0.30901699 -0.95105652
g Cylinder1_default
usemtl default
s 1
f 1// 4// 2//
f 1// 6// 5//
f 2// 4// 3//
f 2// 6// 1//
f 2// 8// 7//
f 3// 8// 2//
f 4// 8// 3//
f 4// 10// 9//
f 5// 4// 1//
f 5// 6// 10//
f 5// 10// 4//
f 6// 9// 10//
f 7// 6// 2//
f 7// 8// 6//
f 9// 6// 8//
f 9// 8// 4//
+54
View File
@@ -0,0 +1,54 @@
# Exported from Wings 3D 2.1.7
mtllib hound.mtl
o Cube1
#17 vertices, 30 faces
v -0.27414249 0.20231664 1.59224039
v 0.24584539 0.20231664 1.59224039
v -0.34489706 0.68997600 -5.9876242e-2
v 0.31659996 0.68997600 -5.9876242e-2
v -0.44658541 0.30520433 -1.66534505
v 0.41828831 0.30520433 -1.66534505
v -1.4148550e-2 -0.39042135 0.70643627
v -1.4148550e-2 -6.9785484e-2 -2.34491358
v -1.4148550e-2 -0.23375148 -0.42106184
v -0.43375565 -1.00192308 -1.66343399
v 0.40545855 -1.00192308 -1.66343399
v -0.60915295 -0.99285579 0.59310583
v 0.58085585 -0.99285579 0.59310583
v -1.4148550e-2 -9.7830546e-3 -1.28127565
v -1.4148550e-2 -5.9388855e-2 0.21094748
v -0.45093850 0.58881440 1.65639487
v 0.42264140 0.58881440 1.65639487
g Cube1_default
usemtl default
s 1
f 1// 7// 2//
f 1// 17// 16//
f 2// 7// 17//
f 2// 17// 1//
f 3// 5// 9//
f 3// 12// 7//
f 3// 17// 4//
f 4// 6// 3//
f 4// 13// 9//
f 4// 17// 7//
f 5// 3// 6//
f 5// 6// 8//
f 6// 11// 8//
f 7// 13// 4//
f 7// 15// 13//
f 7// 16// 3//
f 8// 10// 5//
f 8// 14// 10//
f 9// 5// 10//
f 9// 6// 4//
f 9// 12// 3//
f 9// 14// 11//
f 9// 15// 12//
f 10// 14// 9//
f 11// 6// 9//
f 11// 14// 8//
f 12// 15// 7//
f 13// 15// 9//
f 16// 7// 1//
f 16// 17// 3//
+54
View File
@@ -0,0 +1,54 @@
# Exported from Wings 3D 2.1.7
mtllib hound_run_1.mtl
o Cube1
#17 vertices, 30 faces
v -0.27414249 0.20231664 1.59224039
v 0.24584539 0.20231664 1.59224039
v -0.34489706 0.68997600 -5.9876242e-2
v 0.31659996 0.68997600 -5.9876242e-2
v -0.44658541 0.30520433 -1.66534505
v 0.41828831 0.30520433 -1.66534505
v -1.4148550e-2 -0.39042135 0.70643627
v -1.4148550e-2 -6.9785484e-2 -2.34491358
v -1.4148550e-2 -0.23375148 -0.42106184
v -0.43375565 -0.93382778 -0.95044611
v 0.40545855 -0.83101396 -0.66615665
v -0.60915295 -0.95696320 0.16541373
v 0.58085585 -0.81540550 -7.0403956e-2
v -1.4148550e-2 -9.7830546e-3 -1.28127565
v -1.4148550e-2 -5.9388855e-2 0.21094748
v -0.45093850 0.58881440 1.65639487
v 0.42264140 0.58881440 1.65639487
g Cube1_default
usemtl default
s 1
f 1// 7// 2//
f 1// 17// 16//
f 2// 7// 17//
f 2// 17// 1//
f 3// 5// 9//
f 3// 12// 7//
f 3// 17// 4//
f 4// 6// 3//
f 4// 13// 9//
f 4// 17// 7//
f 5// 3// 6//
f 5// 6// 8//
f 6// 11// 8//
f 7// 13// 4//
f 7// 15// 13//
f 7// 16// 3//
f 8// 10// 5//
f 8// 14// 10//
f 9// 5// 10//
f 9// 6// 4//
f 9// 12// 3//
f 9// 14// 11//
f 9// 15// 12//
f 10// 14// 9//
f 11// 6// 9//
f 11// 14// 8//
f 12// 15// 7//
f 13// 15// 9//
f 16// 7// 1//
f 16// 17// 3//
+54
View File
@@ -0,0 +1,54 @@
# Exported from Wings 3D 2.1.7
mtllib hound_run_2.mtl
o Cube1
#17 vertices, 30 faces
v -0.27414249 0.32374765 1.53588567
v 0.24584539 0.32374765 1.53588567
v -0.34489706 0.87178707 -2.8008988e-2
v 0.31659996 0.87178707 -2.8008988e-2
v -0.44658541 0.48701540 -1.63347780
v 0.41828831 0.48701540 -1.63347780
v -1.4148550e-2 -0.20861028 0.73830352
v -1.4148550e-2 0.11202559 -2.31304633
v -1.4148550e-2 -5.1940403e-2 -0.38919459
v -0.43375565 -0.70455128 -2.48896361
v 0.40545855 -0.68576637 -2.05640756
v -0.60915295 -0.59619234 1.41092021
v 0.58085585 -0.54855918 1.19422288
v -1.4148550e-2 0.17202802 -1.24940840
v -1.4148550e-2 0.12242222 0.24281474
v -0.45093850 0.71024542 1.60004015
v 0.42264140 0.71024542 1.60004015
g Cube1_default
usemtl default
s 1
f 1// 7// 2//
f 1// 17// 16//
f 2// 7// 17//
f 2// 17// 1//
f 3// 5// 9//
f 3// 12// 7//
f 3// 17// 4//
f 4// 6// 3//
f 4// 13// 9//
f 4// 17// 7//
f 5// 3// 6//
f 5// 6// 8//
f 6// 11// 8//
f 7// 13// 4//
f 7// 15// 13//
f 7// 16// 3//
f 8// 10// 5//
f 8// 14// 10//
f 9// 5// 10//
f 9// 6// 4//
f 9// 12// 3//
f 9// 14// 11//
f 9// 15// 12//
f 10// 14// 9//
f 11// 6// 9//
f 11// 14// 8//
f 12// 15// 7//
f 13// 15// 9//
f 16// 7// 1//
f 16// 17// 3//
+46
View File
@@ -0,0 +1,46 @@
# Exported from Wings 3D 2.1.7
mtllib nailgun.mtl
o Cube1
#17 vertices, 22 faces
v -0.49808545 7.0738526e-4 -0.35027558
v 0.73207036 7.0738526e-4 -0.35027558
v -0.49808545 7.0738526e-4 0.23452442
v 0.73207036 7.0738526e-4 0.23452442
v 0.73207036 -0.70631483 -5.7875579e-2
v 0.93949391 -0.16061271 0.49790863
v -1.14322716 -0.16061271 0.49790863
v 0.93949391 0.26298729 0.49790863
v -1.14322716 0.26298729 0.49790863
v 0.93949391 3.7539161e-2 9.6285961e-2
v -1.14322716 3.7539161e-2 9.6285961e-2
v 0.93949391 -0.16061271 -0.61738635
v -1.14322716 -0.16061271 -0.61738635
v 0.93949391 0.26298729 -0.61738635
v -1.14322716 0.26298729 -0.61738635
v 0.93949391 3.7539161e-2 -0.21576367
v -1.14322716 3.7539161e-2 -0.21576367
g Cube1_default
usemtl default
s 1
f 1// 3// 2//
f 1// 5// 3//
f 2// 5// 1//
f 3// 4// 2//
f 3// 5// 4//
f 4// 5// 2//
f 6// 8// 7//
f 6// 10// 8//
f 7// 10// 6//
f 7// 11// 10//
f 8// 9// 7//
f 8// 10// 9//
f 9// 11// 7//
f 10// 11// 9//
f 12// 15// 14//
f 12// 17// 13//
f 13// 15// 12//
f 13// 17// 15//
f 14// 16// 12//
f 14// 17// 16//
f 15// 17// 14//
f 16// 17// 12//
+55
View File
@@ -0,0 +1,55 @@
# Exported from Wings 3D 2.1.7
mtllib q.mtl
o entity0_brush3
#25 vertices, 23 faces
v -1.47660400 0.89508300 -2.93151200
v -4.41993200 2.36674700 -2.93151100
v 1.46672400 0.89508300 -2.93151200
v 4.41005200 2.36674700 -2.93151100
v 1.46672400 -2.78407800 -2.93151200
v 1.46672400 -7.19907000 -2.93151200
v 7.35338000 -0.57658200 -2.93151200
v 10.29670800 -2.78407800 -2.93151200
v 7.35338000 -0.57658200 2.95514500
v 5.88171600 15.61172400 -2.93151100
v 11.03254000 6.78173800 -2.93151100
v 11.03254000 6.78173800 2.95514500
v 13.24003700 7.51757000 -2.93151100
v -7.36326000 -0.57658200 -2.93151200
v -10.30658800 -2.78407800 -2.93151200
v -7.36326000 -0.57658200 2.95514500
v -1.47660400 -7.19907000 -2.93151200
v -1.47660400 -2.78407800 -2.93151200
v -13.24991700 7.51757000 -2.93151100
v -11.04242000 6.78173800 2.95514500
v -11.04242000 6.78173800 -2.93151100
v -5.89159600 15.61172400 -2.93151100
v -1.47660400 -9.40656600 -2.93151200
v -4.9400000e-3 -18.23655100 -2.93151200
v 1.46672400 -9.40656600 -2.93151200
g entity0_brush3_default
usemtl default
s 1
f 1// 4// 3//
f 1// 25// 23//
f 2// 4// 1//
f 3// 25// 1//
f 5// 7// 6//
f 5// 9// 7//
f 7// 8// 6//
f 7// 11// 8//
f 9// 11// 7//
f 9// 12// 11//
f 10// 13// 11//
f 11// 12// 10//
f 11// 13// 8//
f 14// 17// 15//
f 14// 18// 17//
f 14// 20// 16//
f 14// 21// 20//
f 15// 21// 14//
f 16// 18// 14//
f 19// 21// 15//
f 19// 22// 21//
f 21// 22// 20//
f 23// 25// 24//
+27
View File
@@ -0,0 +1,27 @@
# Exported from Wings 3D 2.1.7
mtllib torch.mtl
o cone2
#4 vertices, 3 faces
v 0.56160000 0.93902222 4.8672177e-17
v -0.28080000 0.93902222 0.48635987
v -0.28080000 0.93902222 -0.48635987
v 0.0000000e+0 2.15875556 0.0000000e+0
g cone2_default
usemtl default
s 1
f 1// 4// 2//
f 2// 4// 3//
f 3// 4// 1//
o cone1
#4 vertices, 4 faces
v 1.00000000 1.00000000 0.0000000e+0
v -0.50000000 1.00000000 0.86602540
v -0.50000000 1.00000000 -0.86602540
v 0.0000000e+0 -1.00000000 0.0000000e+0
g cone1_default
usemtl default
s 1
f 5// 7// 6//
f 5// 8// 7//
f 6// 8// 5//
f 7// 8// 6//
+27
View File
@@ -0,0 +1,27 @@
# Exported from Wings 3D 2.1.7
mtllib torch2.mtl
o cone2
#4 vertices, 3 faces
v 0.56160000 0.93902222 4.8672177e-17
v -0.28080000 0.93902222 0.48635987
v -0.28080000 0.93902222 -0.48635987
v 0.37141710 2.34754042 -0.37242647
g cone2_default
usemtl default
s 1
f 1// 4// 2//
f 2// 4// 3//
f 3// 4// 1//
o cone1
#4 vertices, 4 faces
v 1.00000000 1.00000000 0.0000000e+0
v -0.50000000 1.00000000 0.86602540
v -0.50000000 1.00000000 -0.86602540
v 0.0000000e+0 -1.00000000 0.0000000e+0
g cone1_default
usemtl default
s 1
f 5// 7// 6//
f 5// 8// 7//
f 6// 8// 5//
f 7// 8// 6//
+27
View File
@@ -0,0 +1,27 @@
# Exported from Wings 3D 2.1.7
mtllib torch_3.mtl
o cone2
#4 vertices, 3 faces
v 0.56160000 0.93902222 4.8672177e-17
v -0.28080000 0.93902222 0.48635987
v -0.28080000 0.93902222 -0.48635987
v 0.10185419 1.89578758 0.31045814
g cone2_default
usemtl default
s 1
f 1// 4// 2//
f 2// 4// 3//
f 3// 4// 1//
o cone1
#4 vertices, 4 faces
v 1.00000000 1.00000000 0.0000000e+0
v -0.50000000 1.00000000 0.86602540
v -0.50000000 1.00000000 -0.86602540
v 0.0000000e+0 -1.00000000 0.0000000e+0
g cone1_default
usemtl default
s 1
f 5// 7// 6//
f 5// 8// 7//
f 6// 8// 5//
f 7// 8// 6//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib unit_fire.mtl
o idle
#36 vertices, 68 faces
v -9.7072365e-2 0.56526052 0.14813200
v -5.1174828e-3 0.58241863 3.6241454e-2
v -7.3432045e-2 0.41571136 8.8730558e-2
v -2.7773366e-3 0.38195589 0.18768463
v -0.16457846 0.28956454 0.14866615
v -0.22256966 0.36820969 6.5821860e-2
v -2.2042426e-3 0.20761891 0.17635056
v -0.16376325 0.24789118 2.7255653e-2
v -3.2828151e-3 -5.9558653e-2 -3.2466908e-2
v -0.11705512 -1.8998070e-2 0.10033982
v -5.2468113e-2 -0.37762396 0.11261478
v -0.12733850 -0.38368582 0.11398771
v -0.11232770 -0.31817914 4.0267488e-2
v -0.18392516 0.29905284 0.36931274
v -0.21375240 0.22504257 0.28526370
v -0.16363894 0.20062898 0.33478576
v -3.1666221e-3 0.58110829 0.22659559
v -4.3491030e-3 0.40731243 4.3715603e-2
v -1.6209207e-3 -0.12923391 0.10318519
v -0.13664447 0.22269181 0.71114277
v -6.2110567e-2 -0.72441694 1.6821488e-2
v -4.2262828e-3 0.60741476 0.13309524
v 8.9237988e-2 0.56599678 0.14622765
v 6.5570994e-2 0.41626067 8.7309755e-2
v 7.8519940e-2 0.28988820 0.20138750
v 0.14416942 0.38159031 0.14693994
v 0.17385655 0.30958720 9.0735796e-2
v 8.1999016e-2 -1.8089508e-2 9.1851922e-2
v 5.1369741e-2 -0.37721362 0.11155341
v 0.12629811 -0.38268350 0.11139519
v 0.10926638 -0.31730344 3.8002490e-2
v 4.7935402e-2 0.20296119 0.35368779
v 9.7607717e-2 0.13718946 0.30493926
v 2.5761572e-2 0.15405508 0.25758703
v -0.28779708 0.20832952 0.44720995
v 6.1792415e-2 -0.72392731 1.5555028e-2
g idle_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 26// 27//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 25// 27// 32//
f 25// 32// 26//
f 26// 18// 24//
f 26// 24// 25//
f 27// 25// 28//
f 27// 26// 34//
f 28// 9// 27//
f 28// 19// 29//
f 28// 31// 9//
f 29// 9// 31//
f 30// 28// 29//
f 30// 36// 31//
f 31// 28// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 34// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib u42-idle.mtl
o idle
#36 vertices, 68 faces
v -9.7072365e-2 0.56526052 0.14813200
v -5.1174828e-3 0.58241863 3.6241454e-2
v -7.3432045e-2 0.41571136 8.8730558e-2
v -2.7773366e-3 0.38195589 0.18768463
v -0.16457846 0.28956454 0.14866615
v -0.22256966 0.36820969 6.5821860e-2
v -2.2042426e-3 0.20761891 0.17635056
v -0.12240829 0.27200369 1.9776168e-2
v -3.2828151e-3 -5.9558653e-2 -3.2466908e-2
v -0.11705512 -1.8998070e-2 0.10033982
v -5.2468113e-2 -0.37762396 0.11261478
v -0.12733850 -0.38368582 0.11398771
v -0.11232770 -0.31817914 4.0267488e-2
v -0.26195311 6.1200154e-2 8.9171027e-2
v -0.27269328 7.0984903e-2 2.8453086e-2
v -0.19946210 8.9864434e-2 3.4271377e-3
v -3.1666221e-3 0.58110829 0.22659559
v -4.3491030e-3 0.40731243 4.3715603e-2
v -1.6209207e-3 -0.12923391 0.10318519
v -0.26237064 -0.27025472 0.23936610
v -6.2110567e-2 -0.72441694 1.6821488e-2
v -4.2262828e-3 0.60741476 0.13309524
v 8.9237988e-2 0.56599678 0.14622765
v 6.5570994e-2 0.41626067 8.7309755e-2
v 0.15891751 0.29084293 0.14535957
v 0.21457992 0.36993722 6.1353587e-2
v 0.11426169 0.27293896 1.7357074e-2
v 0.11285624 -1.8089508e-2 9.7989805e-2
v 5.1369741e-2 -0.37721362 0.11155341
v 0.12629811 -0.38268350 0.11139519
v 0.10926638 -0.31730344 3.8002490e-2
v 0.25685736 6.3250386e-2 8.3868066e-2
v 0.26627654 7.3114801e-2 2.2944069e-2
v 0.19240220 9.1413002e-2 -5.7825739e-4
v 0.26296420 -0.26817871 0.23399645
v 6.1792415e-2 -0.72392731 1.5555028e-2
g idle_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 26// 27//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 25// 27// 32//
f 25// 32// 26//
f 26// 18// 24//
f 26// 24// 25//
f 27// 25// 28//
f 27// 26// 34//
f 28// 9// 27//
f 28// 19// 29//
f 28// 31// 9//
f 29// 9// 31//
f 30// 28// 29//
f 30// 36// 31//
f 31// 28// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 34// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib u42-run-1.mtl
o run_1
#36 vertices, 68 faces
v -9.5272919e-2 0.58320065 0.23895644
v -3.3180374e-3 0.62435371 0.13350350
v -7.1632599e-2 0.45021114 0.14836243
v -9.7789112e-4 0.39568245 0.23756984
v -0.17307984 0.32466544 0.13174644
v -0.20495298 0.42235179 5.5243521e-2
v -2.6982217e-2 0.25733845 0.18552924
v -9.5805943e-2 0.33183820 1.9155900e-2
v -3.2828150e-3 1.1936972e-2 -2.9445966e-2
v -0.11705512 5.2497554e-2 0.10336076
v -5.5121527e-2 -0.21726415 0.30762026
v -0.12999191 -0.21814619 0.31377274
v -0.11393341 -0.25917247 0.23816510
v -0.23980691 0.24937442 -6.0965392e-2
v -0.23273167 0.23590209 -0.12151453
v -0.15540189 0.25233529 -0.13129183
v -1.3671766e-3 0.58155046 0.31898745
v -2.5496575e-3 0.45183419 0.10259941
v -1.6209207e-3 -5.7738288e-2 0.10620614
v -0.22503228 -2.6466381e-2 0.19543165
v -6.2140773e-2 -0.49248177 0.54917297
v -2.4268373e-3 0.62761985 0.23347747
v 9.1037433e-2 0.58433460 0.23725856
v 6.7370439e-2 0.45105716 0.14709568
v 0.13784712 0.30642002 0.21923207
v 0.21521175 0.39769622 0.17346542
v 0.13166854 0.31848984 8.3160476e-2
v 0.11285624 5.3406116e-2 0.10101075
v 5.6280724e-2 -0.26007502 -2.0635962e-2
v 0.13120909 -0.26515311 -2.2674963e-2
v 0.11417736 -0.17843586 -6.8947620e-2
v 0.21505817 0.24739267 0.31347123
v 0.24150950 0.20354683 0.27775525
v 0.18039399 0.17439735 0.24201011
v 8.5574373e-2 0.14637362 0.54237953
v 8.9212783e-2 -0.30204016 -0.43882211
g run_1_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 26// 27//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 25// 27// 32//
f 25// 32// 26//
f 26// 18// 24//
f 26// 24// 25//
f 27// 25// 28//
f 27// 26// 34//
f 28// 9// 27//
f 28// 19// 29//
f 29// 9// 31//
f 30// 9// 28//
f 30// 28// 29//
f 30// 36// 31//
f 31// 9// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 34// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib u42-run-2.mtl
o run_2
#36 vertices, 68 faces
v -9.4991472e-2 0.52373540 0.23964533
v -3.0365897e-3 0.56488846 0.13419239
v -7.1351151e-2 0.39074589 0.14905133
v -6.9644348e-4 0.33621720 0.23825873
v -0.17267823 0.26520019 0.15406434
v -0.21436437 0.36288654 8.2434335e-2
v -2.0762743e-2 0.19787320 0.18810538
v -0.11093143 0.27237295 3.2264433e-2
v -3.0013674e-3 -4.7528279e-2 -2.8757072e-2
v -0.11677367 -6.9676967e-3 0.10404965
v -6.1941435e-2 -0.36213984 0.21486963
v -0.13681182 -0.36634357 0.21944779
v -0.12075331 -0.35747908 0.13388442
v -0.27214113 0.16564009 1.8179960e-2
v -0.27311477 0.14229232 -3.9713862e-2
v -0.19775045 0.15521696 -6.2045768e-2
v -1.0857290e-3 0.52208521 0.31967635
v -2.2682098e-3 0.39236894 0.10328831
v -1.3394730e-3 -0.11720354 0.10689503
v -0.21276320 -3.2220363e-2 0.25209224
v -9.3144493e-2 -0.70483668 0.17970538
v -2.1453897e-3 0.56815460 0.23416636
v 9.1318881e-2 0.52486934 0.23794745
v 6.7651887e-2 0.39159191 0.14778457
v 0.14707201 0.24695477 0.19977100
v 0.21772350 0.33823097 0.14419908
v 0.12299816 0.25902459 6.5703468e-2
v 0.11313769 -6.0591343e-3 0.10169964
v 5.6562172e-2 -0.34172597 8.3847930e-2
v 0.13149054 -0.34717436 8.4357499e-2
v 0.11445881 -0.29122596 3.5440300e-3
v 0.25704387 0.13904658 0.16957344
v 0.27855275 8.9755721e-2 0.13786319
v 0.21325613 5.6754116e-2 0.11528535
v 0.15532922 -3.1631277e-2 0.40939898
v 8.9494231e-2 -0.52086355 -0.28286119
g run_2_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 26// 27//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 25// 27// 32//
f 25// 32// 26//
f 26// 18// 24//
f 26// 24// 25//
f 27// 25// 28//
f 27// 26// 34//
f 28// 9// 27//
f 28// 19// 29//
f 29// 9// 31//
f 30// 9// 28//
f 30// 28// 29//
f 30// 36// 31//
f 31// 9// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 34// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib u42-run-3.mtl
o run_3
#36 vertices, 68 faces
v -9.7534954e-2 0.58537971 0.23727152
v -5.5800717e-3 0.62207596 0.13434820
v -7.3894633e-2 0.44530355 0.15807461
v -3.2399255e-3 0.39839816 0.25151563
v -0.14811650 0.30785729 0.23172695
v -0.22184769 0.39518773 0.17386227
v 2.9874070e-2 0.25219372 0.18896602
v -0.13324204 0.30880016 9.5796106e-2
v -3.0261434e-3 1.2834357e-2 -2.8933793e-2
v -0.10962990 5.0988036e-2 0.10517160
v -4.1698825e-2 -0.26559037 -1.8795665e-2
v -0.11656921 -0.27177960 -1.9365307e-2
v -0.10155841 -0.18668084 -6.9205025e-2
v -0.24752637 0.24555925 0.31401265
v -0.27201893 0.22587890 0.26943932
v -0.20708582 0.17323236 0.24465303
v -3.6292110e-3 0.58033840 0.31604327
v -4.8116918e-3 0.45317336 0.10171045
v 4.8726959e-5 -5.7632780e-2 0.10082565
v -0.15387665 0.14921342 0.53804817
v -8.6656471e-2 -0.30440046 -0.43683459
v -4.6888717e-3 0.62918786 0.22811015
v 8.8775399e-2 0.58636824 0.23548505
v 6.5108405e-2 0.45331084 0.14611670
v 0.17212974 0.33058228 0.13766338
v 0.20940429 0.42167997 5.5669264e-2
v 0.10220107 0.32864714 2.0166273e-2
v 0.11389002 5.2669771e-2 9.9938702e-2
v 5.2010784e-2 -0.22039388 0.29971756
v 0.12924803 -0.21231946 0.30276019
v 0.11527123 -0.26319363 0.23743150
v 0.22868222 0.25165315 -6.1196965e-2
v 0.22720815 0.23037277 -0.11930067
v 0.15008199 0.25256058 -0.13060470
v 0.21252622 -3.0157464e-2 0.20240516
v 6.2049086e-2 -0.49335414 0.55060042
g run_3_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 24// 27// 18//
f 25// 27// 32//
f 25// 32// 26//
f 26// 24// 25//
f 26// 27// 24//
f 27// 25// 28//
f 27// 33// 34//
f 28// 9// 27//
f 28// 19// 29//
f 28// 31// 9//
f 29// 9// 31//
f 30// 28// 29//
f 30// 36// 31//
f 31// 28// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 27// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
+111
View File
@@ -0,0 +1,111 @@
# Exported from Wings 3D 2.1.5
mtllib u42-run-4.mtl
o run_4
#36 vertices, 68 faces
v -8.7448640e-2 0.52260968 0.23811789
v 1.1106415e-2 0.56619990 0.13601636
v -5.6652628e-2 0.39253244 0.14723325
v 4.7983532e-3 0.33685461 0.23791788
v -0.14201056 0.24676130 0.19890273
v -0.21395473 0.33766742 0.14321628
v 1.5828108e-2 0.25132493 0.18909199
v -0.11559322 0.25643607 6.3974881e-2
v 5.8659423e-3 -4.6614671e-2 -2.8852696e-2
v -0.12367586 5.0119248e-2 0.10529757
v -5.9869043e-2 -0.34160511 8.3280232e-2
v -0.13473943 -0.34779433 8.2710589e-2
v -0.11085266 -0.29407513 4.8260947e-3
v -0.25132952 0.13731816 0.17143274
v -0.27232641 8.9198561e-2 0.13324630
v -0.20953794 5.7890222e-2 0.11187209
v 5.7439754e-3 0.52158520 0.32087826
v 1.4994713e-2 0.38418936 0.11355618
v 9.5050338e-3 -0.11737256 9.6571469e-2
v -0.15147117 -2.8967763e-2 0.40656236
v -8.6836875e-2 -0.51910125 -0.27711023
v 8.1007774e-3 0.56742535 0.23507916
v 0.10858180 0.52745008 0.23670572
v 7.4975063e-2 0.38772831 0.14527201
v 0.17898781 0.26745693 0.15584951
v 0.22056911 0.36000702 8.3859511e-2
v 0.11885495 0.26917230 2.9631907e-2
v 9.9844063e-2 4.2294354e-2 9.3354108e-2
v 6.9120406e-2 -0.36382724 0.20320527
v 0.14171710 -0.36592997 0.21060239
v 0.12317708 -0.35863673 0.13900796
v 0.27620662 0.16435731 1.6758612e-2
v 0.28008560 0.14336818 -3.9632239e-2
v 0.20394231 0.15390316 -6.2704812e-2
v 0.21981481 -3.0190650e-2 0.24883182
v 9.2836181e-2 -0.69912263 0.18131087
g run_4_default
usemtl default
s 1
f 1// 22// 2//
f 2// 3// 1//
f 2// 24// 18//
f 3// 4// 1//
f 3// 8// 6//
f 4// 23// 17//
f 5// 3// 6//
f 5// 4// 3//
f 5// 10// 7//
f 6// 8// 15//
f 6// 14// 5//
f 7// 4// 5//
f 7// 25// 4//
f 7// 28// 25//
f 8// 9// 10//
f 8// 14// 16//
f 8// 18// 9//
f 9// 13// 10//
f 9// 29// 19//
f 10// 5// 8//
f 10// 19// 7//
f 11// 10// 12//
f 11// 12// 21//
f 11// 19// 10//
f 11// 21// 13//
f 12// 10// 13//
f 13// 9// 11//
f 13// 21// 12//
f 14// 6// 15//
f 14// 8// 5//
f 15// 20// 14//
f 16// 15// 8//
f 16// 20// 15//
f 17// 1// 4//
f 17// 22// 1//
f 17// 23// 22//
f 18// 3// 2//
f 18// 8// 3//
f 18// 26// 27//
f 18// 27// 9//
f 19// 11// 9//
f 19// 28// 7//
f 20// 16// 14//
f 22// 23// 2//
f 23// 4// 24//
f 23// 24// 2//
f 24// 4// 25//
f 25// 27// 32//
f 25// 32// 26//
f 26// 18// 24//
f 26// 24// 25//
f 27// 25// 28//
f 27// 26// 34//
f 28// 9// 27//
f 28// 19// 29//
f 29// 9// 31//
f 30// 9// 28//
f 30// 28// 29//
f 30// 36// 31//
f 31// 9// 30//
f 31// 36// 29//
f 32// 34// 35//
f 32// 35// 33//
f 33// 26// 32//
f 33// 34// 26//
f 33// 35// 34//
f 34// 32// 27//
f 36// 30// 29//
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Executable
+124
View File
@@ -0,0 +1,124 @@
set -e
# Compile the pack_map
gcc -std=c99 pack_map.c -lm -o pack_map
# Pack maps
./pack_map assets/maps/m1.map build/m1.plb
./pack_map assets/maps/m2.map build/m2.plb
# Concat all maps into one file
cat \
build/m1.plb \
build/m2.plb \
> build/l
# Pack models
php pack_model.php assets/models/boulder.obj build/boulder.rmf
php pack_model.php assets/models/q.obj build/q.rmf
php pack_model.php assets/models/grenade.obj build/grenade.rmf
php pack_model.php \
assets/models/hound_run_1.obj \
assets/models/hound_run_2.obj \
build/hound.rmf
php pack_model.php \
assets/models/unit_idle.obj \
assets/models/unit_run_1.obj \
assets/models/unit_run_2.obj \
assets/models/unit_run_3.obj \
assets/models/unit_run_4.obj \
assets/models/unit_fire.obj \
build/unit.rmf
php pack_model.php assets/models/box.obj build/box.rmf
php pack_model.php assets/models/nailgun.obj build/nailgun.rmf
php pack_model.php \
assets/models/torch_1.obj \
assets/models/torch_2.obj \
assets/models/torch_3.obj \
build/torch.rmf
# Concat all models into one file
cat \
build/boulder.rmf \
build/unit.rmf \
build/grenade.rmf \
build/q.rmf \
build/hound.rmf \
build/box.rmf \
build/nailgun.rmf \
build/torch.rmf \
> build/m
# Concat js Source
cat \
source/wrap_pre.js \
source/document.js \
source/textures.js \
source/music.js \
source/audio.js \
source/input.js \
source/ttt.js \
source/math_utils.js \
source/renderer.js \
source/model.js \
source/map.js \
source/entity.js \
source/entity_player.js \
source/entity_door.js \
source/entity_light.js \
source/entity_torch.js \
source/entity_barrel.js \
source/entity_particle.js \
source/entity_projectile_grenade.js \
source/entity_projectile_nail.js \
source/entity_projectile_plasma.js \
source/entity_projectile_gib.js \
source/entity_projectile_shell.js \
source/entity_enemy.js \
source/entity_enemy_grunt.js \
source/entity_enemy_enforcer.js \
source/entity_enemy_ogre.js \
source/entity_enemy_zombie.js \
source/entity_enemy_hound.js \
source/entity_pickup.js \
source/entity_pickup_key.js \
source/entity_pickup_nailgun.js \
source/entity_pickup_grenadelauncher.js \
source/entity_pickup_health.js \
source/entity_pickup_nails.js \
source/entity_pickup_grenades.js \
source/entity_trigger_level.js \
source/weapons.js \
source/game.js \
source/main.js \
source/wrap_post.js \
> build/game.js
# Compress WebGL calls, remove DEBUG[...]
php pack_js.php build/game.js > build/game.packed.js
# Uglify JS
npx uglify-js build/game.packed.js \
--compress --mangle toplevel -c --beautify --mangle-props regex=/^_/ \
-o build/game.min.beauty.js
npx uglify-js build/game.packed.js \
--compress --mangle toplevel --mangle-props regex=/^_/ \
-o build/game.min.js
npx roadroller -Zab14 -Zlr1113 -Zmd16 -Zpr14 -S0,1,2,3,6,7,13,21,25,50,235,333 build/game.min.js -o build/game.roadrolled.js
# Embed source into HTML
sed -e '/GAME_SOURCE/{r build/game.roadrolled.js' -e 'd}' source/html_template.html > build/index.html
# Build ZIP
rm -f -- build/game.zip
cd build
zip game.zip index.html l m
cd ..
advzip -z -4 build/game.zip
ls -la build/
+1
View File
@@ -0,0 +1 @@
dummy
Executable
+55
View File
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>Q1K3</title>
</head>
<body>
<div id="d"></div>
<script type="text/javascript" src="source/document.js"></script>
<script type="text/javascript" src="source/ttt.js"></script>
<script type="text/javascript" src="source/audio.js"></script>
<script type="text/javascript" src="source/math_utils.js"></script>
<script type="text/javascript" src="source/model.js"></script>
<script type="text/javascript" src="source/map.js"></script>
<script type="text/javascript" src="source/renderer.js"></script>
<script type="text/javascript" src="source/input.js"></script>
<script type="text/javascript" src="source/weapons.js"></script>
<script type="text/javascript" src="source/entity.js"></script>
<script type="text/javascript" src="source/entity_player.js"></script>
<script type="text/javascript" src="source/entity_light.js"></script>
<script type="text/javascript" src="source/entity_torch.js"></script>
<script type="text/javascript" src="source/entity_door.js"></script>
<script type="text/javascript" src="source/entity_barrel.js"></script>
<script type="text/javascript" src="source/entity_particle.js"></script>
<script type="text/javascript" src="source/entity_projectile_grenade.js"></script>
<script type="text/javascript" src="source/entity_projectile_nail.js"></script>
<script type="text/javascript" src="source/entity_projectile_shell.js"></script>
<script type="text/javascript" src="source/entity_projectile_plasma.js"></script>
<script type="text/javascript" src="source/entity_projectile_gib.js"></script>
<script type="text/javascript" src="source/entity_enemy.js"></script>
<script type="text/javascript" src="source/entity_enemy_grunt.js"></script>
<script type="text/javascript" src="source/entity_enemy_enforcer.js"></script>
<script type="text/javascript" src="source/entity_enemy_ogre.js"></script>
<script type="text/javascript" src="source/entity_enemy_zombie.js"></script>
<script type="text/javascript" src="source/entity_enemy_hound.js"></script>
<script type="text/javascript" src="source/entity_pickup.js"></script>
<script type="text/javascript" src="source/entity_pickup_key.js"></script>
<script type="text/javascript" src="source/entity_pickup_nailgun.js"></script>
<script type="text/javascript" src="source/entity_pickup_grenadelauncher.js"></script>
<script type="text/javascript" src="source/entity_pickup_health.js"></script>
<script type="text/javascript" src="source/entity_pickup_nails.js"></script>
<script type="text/javascript" src="source/entity_pickup_grenades.js"></script>
<script type="text/javascript" src="source/entity_trigger_level.js"></script>
<script type="text/javascript" src="source/textures.js"></script>
<script type="text/javascript" src="source/music.js"></script>
<script type="text/javascript" src="source/game.js"></script>
<script type="text/javascript" src="source/main.js"></script>
</body></html>
+449
View File
@@ -0,0 +1,449 @@
<?php
$WEBGL_REPLACE = [
'ACTIVE_ATTRIBUTES' => 35721,
'ACTIVE_ATTRIBUTE_MAX_LENGTH' => 35722,
'ACTIVE_TEXTURE' => 34016,
'ACTIVE_UNIFORMS' => 35718,
'ACTIVE_UNIFORM_MAX_LENGTH' => 35719,
'ALIASED_LINE_WIDTH_RANGE' => 33902,
'ALIASED_POINT_SIZE_RANGE' => 33901,
'ALPHA' => 6406,
'ALPHA_BITS' => 3413,
'ALWAYS' => 519,
'ARRAY_BUFFER' => 34962,
'ARRAY_BUFFER_BINDING' => 34964,
'ATTACHED_SHADERS' => 35717,
'BACK' => 1029,
'BLEND' => 3042,
'BLEND_COLOR' => 32773,
'BLEND_DST_ALPHA' => 32970,
'BLEND_DST_RGB' => 32968,
'BLEND_EQUATION' => 32777,
'BLEND_EQUATION_ALPHA' => 34877,
'BLEND_EQUATION_RGB' => 32777,
'BLEND_SRC_ALPHA' => 32971,
'BLEND_SRC_RGB' => 32969,
'BLUE_BITS' => 3412,
'BOOL' => 35670,
'BOOL_VEC2' => 35671,
'BOOL_VEC3' => 35672,
'BOOL_VEC4' => 35673,
'BROWSER_DEFAULT_WEBGL' => 37444,
'BUFFER_SIZE' => 34660,
'BUFFER_USAGE' => 34661,
'BYTE' => 5120,
'CCW' => 2305,
'CLAMP_TO_EDGE' => 33071,
'COLOR_ATTACHMENT0' => 36064,
'COLOR_BUFFER_BIT' => 16384,
'COLOR_CLEAR_VALUE' => 3106,
'COLOR_WRITEMASK' => 3107,
'COMPILE_STATUS' => 35713,
'COMPRESSED_TEXTURE_FORMATS' => 34467,
'CONSTANT_ALPHA' => 32771,
'CONSTANT_COLOR' => 32769,
'CONTEXT_LOST_WEBGL' => 37442,
'CULL_FACE' => 2884,
'CULL_FACE_MODE' => 2885,
'CURRENT_PROGRAM' => 35725,
'CURRENT_VERTEX_ATTRIB' => 34342,
'CW' => 2304,
'DECR' => 7683,
'DECR_WRAP' => 34056,
'DELETE_STATUS' => 35712,
'DEPTH_ATTACHMENT' => 36096,
'DEPTH_BITS' => 3414,
'DEPTH_BUFFER_BIT' => 256,
'DEPTH_CLEAR_VALUE' => 2931,
'DEPTH_COMPONENT' => 6402,
'DEPTH_COMPONENT16' => 33189,
'DEPTH_FUNC' => 2932,
'DEPTH_RANGE' => 2928,
'DEPTH_STENCIL' => 34041,
'DEPTH_STENCIL_ATTACHMENT' => 33306,
'DEPTH_TEST' => 2929,
'DEPTH_WRITEMASK' => 2930,
'DITHER' => 3024,
'DONT_CARE' => 4352,
'DST_ALPHA' => 772,
'DST_COLOR' => 774,
'DYNAMIC_DRAW' => 35048,
'ELEMENT_ARRAY_BUFFER' => 34963,
'ELEMENT_ARRAY_BUFFER_BINDING' => 34965,
'EQUAL' => 514,
'FASTEST' => 4353,
'FLOAT' => 5126,
'FLOAT_MAT2' => 35674,
'FLOAT_MAT3' => 35675,
'FLOAT_MAT4' => 35676,
'FLOAT_VEC2' => 35664,
'FLOAT_VEC3' => 35665,
'FLOAT_VEC4' => 35666,
'FRAGMENT_SHADER' => 35632,
'FRAMEBUFFER' => 36160,
'FRAMEBUFFER_ATTACHMENT_OBJECT_NAME' => 36049,
'FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE' => 36048,
'FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE' => 36051,
'FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL' => 36050,
'FRAMEBUFFER_BINDING' => 36006,
'FRAMEBUFFER_COMPLETE' => 36053,
'FRAMEBUFFER_INCOMPLETE_ATTACHMENT' => 36054,
'FRAMEBUFFER_INCOMPLETE_DIMENSIONS' => 36057,
'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT' => 36055,
'FRAMEBUFFER_UNSUPPORTED' => 36061,
'FRONT' => 1028,
'FRONT_AND_BACK' => 1032,
'FRONT_FACE' => 2886,
'FUNC_ADD' => 32774,
'FUNC_REVERSE_SUBTRACT' => 32779,
'FUNC_SUBTRACT' => 32778,
'GENERATE_MIPMAP_HINT' => 33170,
'GEQUAL' => 518,
'GREATER' => 516,
'GREEN_BITS' => 3411,
'HIGH_FLOAT' => 36338,
'HIGH_INT' => 36341,
'INCR' => 7682,
'INCR_WRAP' => 34055,
'INFO_LOG_LENGTH' => 35716,
'INT' => 5124,
'INT_VEC2' => 35667,
'INT_VEC3' => 35668,
'INT_VEC4' => 35669,
'INVALID_ENUM' => 1280,
'INVALID_FRAMEBUFFER_OPERATION' => 1286,
'INVALID_OPERATION' => 1282,
'INVALID_VALUE' => 1281,
'INVERT' => 5386,
'KEEP' => 7680,
'LEQUAL' => 515,
'LESS' => 513,
'LINEAR' => 9729,
'LINEAR_MIPMAP_LINEAR' => 9987,
'LINEAR_MIPMAP_NEAREST' => 9985,
'LINES' => 1,
'LINE_LOOP' => 2,
'LINE_STRIP' => 3,
'LINE_WIDTH' => 2849,
'LINK_STATUS' => 35714,
'LOW_FLOAT' => 36336,
'LOW_INT' => 36339,
'LUMINANCE' => 6409,
'LUMINANCE_ALPHA' => 6410,
'MAX_COMBINED_TEXTURE_IMAGE_UNITS' => 35661,
'MAX_CUBE_MAP_TEXTURE_SIZE' => 34076,
'MAX_FRAGMENT_UNIFORM_VECTORS' => 36349,
'MAX_RENDERBUFFER_SIZE' => 34024,
'MAX_TEXTURE_IMAGE_UNITS' => 34930,
'MAX_TEXTURE_SIZE' => 3379,
'MAX_VARYING_VECTORS' => 36348,
'MAX_VERTEX_ATTRIBS' => 34921,
'MAX_VERTEX_TEXTURE_IMAGE_UNITS' => 35660,
'MAX_VERTEX_UNIFORM_VECTORS' => 36347,
'MAX_VIEWPORT_DIMS' => 3386,
'MEDIUM_FLOAT' => 36337,
'MEDIUM_INT' => 36340,
'MIRRORED_REPEAT' => 33648,
'NEAREST' => 9728,
'NEAREST_MIPMAP_LINEAR' => 9986,
'NEAREST_MIPMAP_NEAREST' => 9984,
'NEVER' => 512,
'NICEST' => 4354,
'NONE' => 0,
'NOTEQUAL' => 517,
'NO_ERROR' => 0,
'NUM_COMPRESSED_TEXTURE_FORMATS' => 34466,
'ONE' => 1,
'ONE_MINUS_CONSTANT_ALPHA' => 32772,
'ONE_MINUS_CONSTANT_COLOR' => 32770,
'ONE_MINUS_DST_ALPHA' => 773,
'ONE_MINUS_DST_COLOR' => 775,
'ONE_MINUS_SRC_ALPHA' => 771,
'ONE_MINUS_SRC_COLOR' => 769,
'OUT_OF_MEMORY' => 1285,
'PACK_ALIGNMENT' => 3333,
'POINTS' => 0,
'POLYGON_OFFSET_FACTOR' => 32824,
'POLYGON_OFFSET_FILL' => 32823,
'POLYGON_OFFSET_UNITS' => 10752,
'RED_BITS' => 3410,
'RENDERBUFFER' => 36161,
'RENDERBUFFER_ALPHA_SIZE' => 36179,
'RENDERBUFFER_BINDING' => 36007,
'RENDERBUFFER_BLUE_SIZE' => 36178,
'RENDERBUFFER_DEPTH_SIZE' => 36180,
'RENDERBUFFER_GREEN_SIZE' => 36177,
'RENDERBUFFER_HEIGHT' => 36163,
'RENDERBUFFER_INTERNAL_FORMAT' => 36164,
'RENDERBUFFER_RED_SIZE' => 36176,
'RENDERBUFFER_STENCIL_SIZE' => 36181,
'RENDERBUFFER_WIDTH' => 36162,
'RENDERER' => 7937,
'REPEAT' => 10497,
'REPLACE' => 7681,
'RGB' => 6407,
'RGB5_A1' => 32855,
'RGB565' => 36194,
'RGBA' => 6408,
'RGBA4' => 32854,
'SAMPLER_2D' => 35678,
'SAMPLER_CUBE' => 35680,
'SAMPLES' => 32937,
'SAMPLE_ALPHA_TO_COVERAGE' => 32926,
'SAMPLE_BUFFERS' => 32936,
'SAMPLE_COVERAGE' => 32928,
'SAMPLE_COVERAGE_INVERT' => 32939,
'SAMPLE_COVERAGE_VALUE' => 32938,
'SCISSOR_BOX' => 3088,
'SCISSOR_TEST' => 3089,
'SHADER_COMPILER' => 36346,
'SHADER_SOURCE_LENGTH' => 35720,
'SHADER_TYPE' => 35663,
'SHADING_LANGUAGE_VERSION' => 35724,
'SHORT' => 5122,
'SRC_ALPHA' => 770,
'SRC_ALPHA_SATURATE' => 776,
'SRC_COLOR' => 768,
'STATIC_DRAW' => 35044,
'STENCIL_ATTACHMENT' => 36128,
'STENCIL_BACK_FAIL' => 34817,
'STENCIL_BACK_FUNC' => 34816,
'STENCIL_BACK_PASS_DEPTH_FAIL' => 34818,
'STENCIL_BACK_PASS_DEPTH_PASS' => 34819,
'STENCIL_BACK_REF' => 36003,
'STENCIL_BACK_VALUE_MASK' => 36004,
'STENCIL_BACK_WRITEMASK' => 36005,
'STENCIL_BITS' => 3415,
'STENCIL_BUFFER_BIT' => 1024,
'STENCIL_CLEAR_VALUE' => 2961,
'STENCIL_FAIL' => 2964,
'STENCIL_FUNC' => 2962,
'STENCIL_INDEX' => 6401,
'STENCIL_INDEX8' => 36168,
'STENCIL_PASS_DEPTH_FAIL' => 2965,
'STENCIL_PASS_DEPTH_PASS' => 2966,
'STENCIL_REF' => 2967,
'STENCIL_TEST' => 2960,
'STENCIL_VALUE_MASK' => 2963,
'STENCIL_WRITEMASK' => 2968,
'STREAM_DRAW' => 35040,
'SUBPIXEL_BITS' => 3408,
'TEXTURE' => 5890,
'TEXTURE0' => 33984,
'TEXTURE1' => 33985,
'TEXTURE2' => 33986,
'TEXTURE3' => 33987,
'TEXTURE4' => 33988,
'TEXTURE5' => 33989,
'TEXTURE6' => 33990,
'TEXTURE7' => 33991,
'TEXTURE8' => 33992,
'TEXTURE9' => 33993,
'TEXTURE10' => 33994,
'TEXTURE11' => 33995,
'TEXTURE12' => 33996,
'TEXTURE13' => 33997,
'TEXTURE14' => 33998,
'TEXTURE15' => 33999,
'TEXTURE16' => 34000,
'TEXTURE17' => 34001,
'TEXTURE18' => 34002,
'TEXTURE19' => 34003,
'TEXTURE20' => 34004,
'TEXTURE21' => 34005,
'TEXTURE22' => 34006,
'TEXTURE23' => 34007,
'TEXTURE24' => 34008,
'TEXTURE25' => 34009,
'TEXTURE26' => 34010,
'TEXTURE27' => 34011,
'TEXTURE28' => 34012,
'TEXTURE29' => 34013,
'TEXTURE30' => 34014,
'TEXTURE31' => 34015,
'TEXTURE_2D' => 3553,
'TEXTURE_BINDING_2D' => 32873,
'TEXTURE_BINDING_CUBE_MAP' => 34068,
'TEXTURE_CUBE_MAP' => 34067,
'TEXTURE_CUBE_MAP_NEGATIVE_X' => 34070,
'TEXTURE_CUBE_MAP_NEGATIVE_Y' => 34072,
'TEXTURE_CUBE_MAP_NEGATIVE_Z' => 34074,
'TEXTURE_CUBE_MAP_POSITIVE_X' => 34069,
'TEXTURE_CUBE_MAP_POSITIVE_Y' => 34071,
'TEXTURE_CUBE_MAP_POSITIVE_Z' => 34073,
'TEXTURE_MAG_FILTER' => 10240,
'TEXTURE_MIN_FILTER' => 10241,
'TEXTURE_WRAP_S' => 10242,
'TEXTURE_WRAP_T' => 10243,
'TRIANGLES' => 4,
'TRIANGLE_FAN' => 6,
'TRIANGLE_STRIP' => 5,
'UNPACK_ALIGNMENT' => 3317,
'UNPACK_COLORSPACE_CONVERSION_WEBGL' => 37443,
'UNPACK_FLIP_Y_WEBGL' => 37440,
'UNPACK_PREMULTIPLY_ALPHA_WEBGL' => 37441,
'UNSIGNED_BYTE' => 5121,
'UNSIGNED_INT' => 5125,
'UNSIGNED_SHORT' => 5123,
'UNSIGNED_SHORT_4_4_4_4' => 32819,
'UNSIGNED_SHORT_5_5_5_1' => 32820,
'UNSIGNED_SHORT_5_6_5' => 33635,
'VALIDATE_STATUS' => 35715,
'VENDOR' => 7936,
'VERSION' => 7938,
'VERTEX_ATTRIB_ARRAY_BUFFER_BINDING' => 34975,
'VERTEX_ATTRIB_ARRAY_ENABLED' => 34338,
'VERTEX_ATTRIB_ARRAY_NORMALIZED' => 34922,
'VERTEX_ATTRIB_ARRAY_POINTER' => 34373,
'VERTEX_ATTRIB_ARRAY_SIZE' => 34339,
'VERTEX_ATTRIB_ARRAY_STRIDE' => 34340,
'VERTEX_ATTRIB_ARRAY_TYPE' => 34341,
'VERTEX_SHADER' => 35633,
'VIEWPORT' => 2978,
'ZERO' => 0,
'activeTexture' => "gl.acT",
'attachShader' => "gl.atS",
'bindAttribLocation' => "gl.biAL",
'bindBuffer' => "gl.biB",
'bindFramebuffer' => "gl.biF",
'bindRenderbuffer' => "gl.biR",
'bindTexture' => "gl.biT",
'blendColor' => "gl.blC",
'blendEquation' => "gl.blE",
'blendEquationSeparate' => "gl.blES",
'blendFunc' => "gl.blF",
'blendFuncSeparate' => "gl.blFS",
'bufferData' => "gl.buD",
'bufferSubData' => "gl.buSD",
'checkFramebufferStatus' => "gl.chFS",
'clear' => "gl.cl",
'clearColor' => "gl.clC",
'clearDepth' => "gl.clD",
'clearStencil' => "gl.clS",
'colorMask' => "gl.coM",
'compileShader' => "gl.coS",
'compressedTexImage2D' => "gl.coTI2D",
'compressedTexSubImage2D' => "gl.coTSI2D",
'copyTexImage2D' => "gl.coTI2D",
'copyTexSubImage2D' => "gl.coTSI2D",
'createBuffer' => "gl.crB",
'createFramebuffer' => "gl.crF",
'createProgram' => "gl.crP",
'createRenderbuffer' => "gl.crR",
'createShader' => "gl.crS",
'createTexture' => "gl.crT",
'cullFace' => "gl.cuF",
'deleteBuffer' => "gl.deB",
'deleteFramebuffer' => "gl.deF",
'deleteProgram' => "gl.deP",
'deleteRenderbuffer' => "gl.deR",
'deleteShader' => "gl.deS",
'deleteTexture' => "gl.deT",
'depthFunc' => "gl.deF",
'depthMask' => "gl.deM",
'depthRange' => "gl.deR",
'detachShader' => "gl.deS",
'disable' => "gl.di",
'disableVertexAttribArray' => "gl.diVAA",
'drawArrays' => "gl.drA",
'drawElements' => "gl.drE",
'enable' => "gl.en",
'enableVertexAttribArray' => "gl.enVAA",
'finish' => "gl.fi",
'flush' => "gl.fl",
'framebufferRenderbuffer' => "gl.frR",
'framebufferTexture2D' => "gl.frT2D",
'frontFace' => "gl.frF",
'generateMipmap' => "gl.geM",
'getActiveAttrib' => "gl.geAA",
'getActiveUniform' => "gl.geAU",
'getAttachedShaders' => "gl.geAS",
'getAttribLocation' => "gl.geAL",
'getBufferParameter' => "gl.geBP",
'getContextAttributes' => "gl.geCA",
'getError' => "gl.geE",
'getExtension' => "gl.geE",
'getFramebufferAttachmentParameter' => "gl.geFAP",
'getParameter' => "gl.geP",
'getProgramParameter' => "gl.gePP",
'getProgramInfoLog' => "gl.gePIL",
'getRenderbufferParameter' => "gl.geRP",
'getShaderParameter' => "gl.geSP",
'getShaderInfoLog' => "gl.geSIL",
'getShaderPrecisionFormat' => "gl.geSPF",
'getShaderSource' => "gl.geSS",
'getSupportedExtensions' => "gl.geSE",
'getTexParameter' => "gl.geTP",
'getUniform' => "gl.geU",
'getUniformLocation' => "gl.geUL",
'getVertexAttrib' => "gl.geVA",
'getVertexAttribOffset' => "gl.geVAO",
'hint' => "gl.hi",
'isBuffer' => "gl.isB",
'isContextLost' => "gl.isCL",
'isEnabled' => "gl.isE",
'isFramebuffer' => "gl.isF",
'isProgram' => "gl.isP",
'isRenderbuffer' => "gl.isR",
'isShader' => "gl.isS",
'isTexture' => "gl.isT",
'lineWidth' => "gl.liW",
'linkProgram' => "gl.liP",
'pixelStorei' => "gl.piS",
'polygonOffset' => "gl.poO",
'readPixels' => "gl.reP",
'renderbufferStorage' => "gl.reS",
'sampleCoverage' => "gl.saC",
'scissor' => "gl.sc",
'shaderSource' => "gl.shS",
'stencilFunc' => "gl.stF",
'stencilFuncSeparate' => "gl.stFS",
'stencilMask' => "gl.stM",
'stencilMaskSeparate' => "gl.stMS",
'stencilOp' => "gl.stO",
'stencilOpSeparate' => "gl.stOS",
'texParameterf' => "gl.teP",
'texParameteri' => "gl.teP",
'texImage2D' => "gl.teI2D",
'texSubImage2D' => "gl.teSI2D",
'uniform1f' => "gl.un1f",
'uniform1fv' => "gl.un1fv",
'uniform1i' => "gl.un1i",
'uniform1iv' => "gl.un1iv",
'uniform2f' => "gl.un2f",
'uniform2fv' => "gl.un2fv",
'uniform2i' => "gl.un2i",
'uniform2iv' => "gl.un2iv",
'uniform3f' => "gl.un3f",
'uniform3fv' => "gl.un3fv",
'uniform3i' => "gl.un3i",
'uniform3iv' => "gl.un3iv",
'uniform4f' => "gl.un4f",
'uniform4fv' => "gl.un4fv",
'uniform4i' => "gl.un4i",
'uniform4iv' => "gl.un4iv",
'uniformMatrix2fv' => "gl.unM2fv",
'uniformMatrix3fv' => "gl.unM3fv",
'uniformMatrix4fv' => "gl.unM4fv",
'useProgram' => "gl.usP",
'validateProgram' => "gl.vaP",
'vertexAttrib1f' => "gl.veA1f",
'vertexAttrib1fv' => "gl.veA1fv",
'vertexAttrib2f' => "gl.veA2f",
'vertexAttrib2fv' => "gl.veA2fv",
'vertexAttrib3f' => "gl.veA3f",
'vertexAttrib3fv' => "gl.veA3fv",
'vertexAttrib4f' => "gl.veA4f",
'vertexAttrib4fv' => "gl.veA4fv",
'vertexAttribPointer' => "gl.veAP",
'viewport' => "gl.vi"
];
$js = file_get_contents($argv[1]);
foreach ($WEBGL_REPLACE as $search => $replace) {
$js = preg_replace('/\bgl\.'.$search.'\b/', $replace, $js);
}
$js = preg_replace('/\/\*DEBUG\[.*?\/\*\]\*\//sm', '', $js);
echo $js;
+1077
View File
File diff suppressed because it is too large Load Diff
Executable
+103
View File
@@ -0,0 +1,103 @@
<?php
$script = array_shift($argv);
/* Convert Wavefront OBJ to Retarded Model Format (RMF)
struct {
u8 num_frames;
u8 num_verts; // per frame
u8 num_indices;
struct {
u8 x, y, z;
} verts[num_frames * num_verts];
struct {
u8 a_address_inc, b_index, c_index;
} indices[num_indices];
} rmf_data;
*/
if (count($argv) < 2 || !file_exists($argv[0])) {
die("Usage: php model_packer.php frame1.obj frame2.obj... outfile.rmf\n");
}
$infiles = array_slice($argv, 0, -1);
$verts = [];
$indices = [];
$max = -INF;
// Find vertices in all files
foreach ($infiles as $file) {
if (!file_exists($file)) {
die("Couldn't load $file\n");
}
echo "Loading $file\n";
foreach (file($file) as $line) {
if (preg_match('#^v (.*?) (.*?) (.*?)$#', $line, $m)) {
$v = [(float)$m[1], (float)$m[2], (float)$m[3]];
$verts[] = $v;
$max = max($max, abs($v[0]), abs($v[1]), abs($v[2]));
}
}
}
// Find indices in first file (we assume the layout is the same in all
// subsequent animation frames).
foreach (file($infiles[0]) as $line) {
if (preg_match('#^f (\d+).*?(\d+).*?(\d+).*?$#', $line, $m)) {
$indices[] = [((int)$m[1])-1, ((int)$m[2])-1, ((int)$m[3])-1];
}
}
// Pack header
$packed =
pack('C', count($infiles)).
pack('C', count($verts)/count($infiles)).
pack('C', count($indices));
// Pack normalized (-15, 15) vertices
foreach ($verts as $i => $v) {
$x = round(($v[0]/$max)*15)+15;
$y = round(($v[1]/$max)*15)+15;
$z = round(($v[2]/$max)*15)+15;
// echo "Vertex $i => ($x, $y, $z)\n";
$packed .=
pack('C', $x).
pack('C', $y).
pack('C', $z);
}
// Pack indices w. vertex index
$a_last_index = 0;
foreach ($indices as $i => $f) {
$a_address_inc = $f[0] - $a_last_index;
if ($a_address_inc > 3) {
die("Face $i index a increment exceeds 2 bits ($a_address_inc)\n");
}
$a_last_index = $f[0];
if ($f[1] > 127 || $f[2] > 127) {
die("Face $i index exceeds 7 bits ({$f[1]}, {$f[1]}, {$f[2]})\n");
}
// echo "Face $i => ({$f[1]}, {$f[1]}, {$f[2]})\n";
// $packed .= pack('v', ($a_address_inc << 14) | ($f[1] << 7) | $f[2]);
$packed .=
pack('C', $a_address_inc).
pack('C', $f[1]).
pack('C', $f[2]);
}
// Write
$packedfile = $argv[count($argv)-1];
file_put_contents($packedfile, $packed);
echo "Wrote $packedfile: ".
count($infiles)." frame(s), ".
count($verts)." verts, ".
count($indices)." indices, ".
strlen($packed)." bytes\n";
+240
View File
@@ -0,0 +1,240 @@
// Gutted for js13k and modified to use Float32 buffers directly
// ~ Dominic Szablewski, phoboslab.org, Sep 2018
// Almost re-written for for jsk13 2019. Oscilators now use a lookup table
// instead of calling functions. This and various other changes result in a
// ~10x performance increase and smaller file size.
// ~ Dominic Szablewski, phoboslab.org, Sep 2019
// Again updated for js13k 2021. Song and sound definitions are now just arrays
// instead of objects.
//
// Sonant-X
//
// Copyr (c) 2014 Nicolas Vanhoren
//
// Sonant-X is a fork of js-sonant by Marcus Geelnard and Jake Taylor. It is
// still published using the same license (zlib license, see below).
//
// Copyr (c) 2011 Marcus Geelnard
// Copyr (c) 2008-2009 Jake Taylor
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
let
audio_ctx,
AUDIO_SAMPLERATE = 44100, // Samples per second
AUDIO_TAB_SIZE = 4096,
AUDIO_TAB_MASK = AUDIO_TAB_SIZE-1,
AUDIO_TAB = new Float32Array(AUDIO_TAB_SIZE*4), // 4 oscilators
audio_init = () => {
// This function needs to be called in response to a user action, as it
// tries to activate the audio context.
audio_ctx = new AudioContext();
audio_ctx.resume();
// Generate the lookup tables
for (let i = 0; i < AUDIO_TAB_SIZE; i++) {
AUDIO_TAB[i ] = Math.sin(i*6.283184/AUDIO_TAB_SIZE); // sin
AUDIO_TAB[i + AUDIO_TAB_SIZE ] = AUDIO_TAB[i] < 0 ? -1 : 1; // square
AUDIO_TAB[i + AUDIO_TAB_SIZE * 2] = i / AUDIO_TAB_SIZE - 0.5; // saw
AUDIO_TAB[i + AUDIO_TAB_SIZE * 3] = i < AUDIO_TAB_SIZE/2 ? (i/(AUDIO_TAB_SIZE/4)) - 1 : 3 - (i/(AUDIO_TAB_SIZE/4)); // tri
}
},
audio_play = (buffer, volume = 1, loop = 0, pan = 0) => {
let gain = audio_ctx.createGain(),
source = audio_ctx.createBufferSource(),
panner = audio_ctx.createStereoPanner();
gain.gain.value = volume;
gain.connect(audio_ctx.destination);
panner.connect(gain);
panner.pan.value = pan;
source.buffer = buffer;
source.loop = loop;
source.connect(panner);
source.start();
},
audio_get_ctx_buffer = (buf_l, buf_r) => {
let buffer = audio_ctx.createBuffer(2, buf_l.length, AUDIO_SAMPLERATE);
buffer.getChannelData(0).set(buf_l);
buffer.getChannelData(1).set(buf_r);
return buffer;
},
audio_generate_sound = (
row_len, note, buf_l, buf_r, write_pos,
// Instrument properties
osc1_oct, osc1_det, osc1_detune, osc1_xenv, osc1_vol, osc1_waveform,
osc2_oct, osc2_det, osc2_detune, osc2_xenv, osc2_vol, osc2_waveform,
noise_fader, attack, sustain, release, master,
fx_filter, fx_freq, fx_resonance, fx_delay_time, fx_delay_amt, fx_pan_freq_p, fx_pan_amt,
lfo_osc1_freq, lfo_fx_freq, lfo_freq_p, lfo_amt, lfo_waveform
) => {
let osc_lfo_offset = lfo_waveform * AUDIO_TAB_SIZE,
osc1_offset = osc1_waveform * AUDIO_TAB_SIZE,
osc2_offset = osc2_waveform * AUDIO_TAB_SIZE,
fx_pan_freq = Math.pow(2, fx_pan_freq_p - 8) / row_len,
lfo_freq = Math.pow(2, lfo_freq_p - 8) / row_len,
c1 = 0,
c2 = 0,
q = fx_resonance / 255,
low = 0,
band = 0,
high = 0,
buf_length = buf_l.length,
num_samples = attack + sustain + release - 1,
osc1_freq =
Math.pow(1.059463094, (note + (osc1_oct - 8) * 12 + osc1_det) - 128)
* 0.00390625 * (1 + 0.0008 * osc1_detune),
osc2_freq =
Math.pow(1.059463094, (note + (osc2_oct - 8) * 12 + osc2_det) - 128)
* 0.00390625 * (1 + 0.0008 * osc2_detune);
for (let j = num_samples; j >= 0; --j) {
let
// Buffer positions
k = j + write_pos,
// LFO
lfor = AUDIO_TAB[osc_lfo_offset + ((k * lfo_freq * AUDIO_TAB_SIZE) & AUDIO_TAB_MASK)] * lfo_amt / 512 + 0.5,
sample = 0,
filter_f = fx_freq,
temp_f,
envelope = 1;
// Envelope
if (j < attack) {
envelope = j / attack;
}
else if (j >= attack + sustain) {
envelope -= (j - attack - sustain) / release;
}
// Oscillator 1
temp_f = osc1_freq;
if (lfo_osc1_freq) {
temp_f *= lfor;
}
if (osc1_xenv) {
temp_f *= envelope * envelope;
}
c1 += temp_f;
sample += AUDIO_TAB[osc1_offset + ((c1 * AUDIO_TAB_SIZE) & AUDIO_TAB_MASK)] * osc1_vol;
// Oscillator 2
temp_f = osc2_freq;
if (osc2_xenv) {
temp_f *= envelope * envelope;
}
c2 += temp_f;
sample += AUDIO_TAB[osc2_offset + ((c2 * AUDIO_TAB_SIZE) & AUDIO_TAB_MASK)] * osc2_vol;
// Noise oscillator
if (noise_fader) {
sample += (2*Math.random()-1) * noise_fader * envelope;
}
sample *= envelope / 255;
// State variable filter
if (lfo_fx_freq) {
filter_f *= lfor;
}
filter_f = 1.5 * AUDIO_TAB[(filter_f * 0.5 / AUDIO_SAMPLERATE * AUDIO_TAB_SIZE) & AUDIO_TAB_MASK];
low += filter_f * band;
high = q * (sample - band) - low;
band += filter_f * high;
sample = [sample, high, low, band, low + high][fx_filter];
// Panning & master volume
temp_f = AUDIO_TAB[(k * fx_pan_freq * AUDIO_TAB_SIZE) & AUDIO_TAB_MASK] * fx_pan_amt / 512 + 0.5;
sample *= 0.00476 * master; // 39 / 8192 = 0.00476
buf_l[k] += sample * (1-temp_f);
buf_r[k] += sample * temp_f;
}
},
audio_create_song = (row_len, pattern_len, song_len, tracks) => {
let num_samples = AUDIO_SAMPLERATE * song_len,
mix_buf_l = new Float32Array(num_samples),
mix_buf_r = new Float32Array(num_samples);
for (let track of tracks) {
let buf_l = new Float32Array(num_samples),
buf_r = new Float32Array(num_samples),
write_pos = 0,
delay_shift = (track[0/*instrument*/][20/*fx_delay_time*/] * row_len) >> 1,
delay_amount = track[0/*instrument*/][21/*fx_delay_amt*/] / 255;
for (let p = 0; p < pattern_len; p++) {
for (let row = 0; row < 32; row++) {
let note = track[2/*notes*/][track[1/*pattern*/][p] - 1]?.[row];
if (note) {
audio_generate_sound(row_len, note, buf_l, buf_r, write_pos, ...track[0/*instrument*/]);
}
write_pos += row_len;
}
}
audio_apply_delay(delay_shift, delay_amount, buf_l, buf_r);
for (let b = 0; b < num_samples; b++) {
mix_buf_l[b] += buf_l[b];
mix_buf_r[b] += buf_r[b];
}
}
return audio_get_ctx_buffer(mix_buf_l, mix_buf_r);
},
audio_create_sound = (note, instrument, row_len = 5605) => {
let delay_shift = (instrument[20/*fx_delay_time*/] * row_len) >> 1,
delay_amount = instrument[21/*fx_delay_amt*/] / 255,
num_samples =
instrument[13/*env_attack*/] +
instrument[14/*env_sustain*/] +
instrument[15/*env_release*/] +
delay_shift * 32 * delay_amount,
buf_l = new Float32Array(num_samples),
buf_r = new Float32Array(num_samples);
audio_generate_sound(row_len, note, buf_l, buf_r, 0, ...instrument);
audio_apply_delay(delay_shift, delay_amount, buf_l, buf_r);
return audio_get_ctx_buffer(buf_l, buf_r);
},
audio_apply_delay = (shift, amount, buf_l, buf_r) => {
for (let i = 0; i < buf_l.length - shift; i++) {
buf_l[i + shift] += buf_r[i] * amount;
buf_r[i + shift] += buf_l[i] * amount;
}
};
+24
View File
@@ -0,0 +1,24 @@
document.body.innerHTML +=
'<style>'+
'*{font-family:sans-serif;}'+
'a,body{color:#fff;background:#111;text-align:center;margin:0;}'+
'#c{display:block;width:100%;image-rendering:optimizeSpeed;image-rendering:pixelated;max-height:100vh;object-fit:contain;}'+
'#g{position:relative;margin:0 0 32px 0;font-weight:bold;}'+
'#ts{position:absolute;bottom:2em;left:0;right:0;font-size:1.2vw;}'+
'#msg{position:absolute;top:8vw;left:0;right:0;font-size:1.2vw;display:none;}'+
'#a,#h{position:absolute;bottom:3%;left:20%;right:0;font-size:3.2vw;}'+
'#h{left:-20%;}'+
'h1{font-size:16vw;margin:0;}'+
'</style>'+
'<div id="g">'+
'<canvas id=c width=320 height=180></canvas>'+
'<div id="ts"><h1>Q1K3</h1>CLICK TO START</div>'+
'<div id="h"></div><div id="a"></div>'+
'<div id="msg"></div>'+
'</div>'+
'<p>MOUSE SPEED: <input id="m" type="range" value=10 min=1 max=50></p>'+
'<p><input id="f" type="button" value="FULLSCREEN"></p>'+
'<p>'+
'code: <a href="https://phoboslab.org">phoboslab.org</a> / music: <a href="http://no-fate.net">no-fate.net</a>'+
'</p>';
+226
View File
@@ -0,0 +1,226 @@
let ENTITY_GROUP_NONE = 0,
ENTITY_GROUP_PLAYER = 1,
ENTITY_GROUP_ENEMY = 2;
class entity_t {
constructor(pos, p1, p2) {
this.a = vec3();
this.v = vec3();
this.p = pos;
this.s = vec3(2,2,2);
this.f = 0;
this._health = 50;
this._dead = 0;
this._die_at = 0;
this._step_height = 0;
this._bounciness = 0;
this._gravity = 1;
this._yaw = 0;
this._pitch = 0;
this._anim = [1, [0]];
this._anim_time = Math.random();
this._on_ground = 0;
this._keep_off_ledges = 0;
this._check_against = ENTITY_GROUP_NONE;
this._stepped_up_at = 0;
this._init(p1, p2);
}
_init(p1, p2) {}
_update() {
if (this._model) {
this._draw_model();
}
}
_update_physics() {
if (this._die_at && this._die_at < game_time) {
this._kill();
return;
}
// Apply Gravity
this.a.y = -1200 * this._gravity;
// Integrate acceleration & friction into velocity
let ff = Math.min(this.f * game_tick, 1);
this.v = vec3_add(
this.v, vec3_sub(
vec3_mulf(this.a, game_tick),
vec3_mul(this.v, vec3(ff, 0, ff))
)
);
// Set up the _check_entities array for entity collisions
this._check_entities = [
[],
game_entities_friendly,
game_entities_enemies
][this._check_against];
// Divide the physics integration into 16 unit steps; otherwise fast
// projectiles may just move through walls.
let
original_step_height = this._step_height,
move_dist = vec3_mulf(this.v, game_tick),
steps = Math.ceil(vec3_length(move_dist) / 16),
move_step_height = vec3_mulf(move_dist, 1/steps);
for (let s = 0; s < steps; s++) {
// Remember last position so we can roll back
let lp = vec3_clone(this.p);
// Integrate velocity into position
this.p = vec3_add(this.p, move_step_height);
// Collision with walls, horizonal
if (this._collides(vec3(this.p.x, lp.y, lp.z))) {
// Can we step up?
if (
!this._step_height || !this._on_ground || this.v.y > 0 ||
this._collides(vec3(this.p.x, lp.y+this._step_height, lp.z))
) {
this._did_collide(0);
this.p.x = lp.x;
this.v.x = -this.v.x * this._bounciness;
}
else {
lp.y += this._step_height;
this._stepped_up_at = game_time;
}
s = steps; // stop after this iteration
}
// Collision with walls, vertical
if (this._collides(vec3(this.p.x, lp.y, this.p.z))) {
// Can we step up?
if (
!this._step_height || !this._on_ground || this.v.y > 0 ||
this._collides(vec3(this.p.x, lp.y+this._step_height, this.p.z))
) {
this._did_collide(2);
this.p.z = lp.z;
this.v.z = -this.v.z * this._bounciness;
}
else {
lp.y += this._step_height;
this._stepped_up_at = game_time;
}
s = steps; // stop after this iteration
}
// Collision with ground/Ceiling
if (this._collides(this.p)) {
this._did_collide(1);
this.p.y = lp.y;
// Only bounce from ground/ceiling if we have enough velocity
let bounce = Math.abs(this.v.y) > 200 ? this._bounciness : 0;
this._on_ground = this.v.y < 0 && !bounce;
this.v.y = -this.v.y * bounce;
s = steps; // stop after this iteration
}
this._step_height = original_step_height;
}
}
_collides(p) {
if (this._dead) {
return;
}
for (let entity of this._check_entities) {
if (vec3_dist(p, entity.p) < this.s.y + entity.s.y) {
// If we collide with an entity set the step height to 0,
// so we don't climb up on its shoulders :/
this._step_height = 0;
this._did_collide_with_entity(entity);
return true;
}
}
// Check if there's no block beneath this point. We want the AI to keep
// off of ledges.
if (
this._on_ground && this._keep_off_ledges &&
!map_block_at(p.x >> 5, (p.y-this.s.y-8) >> 4, p.z >>5) &&
!map_block_at(p.x >> 5, (p.y-this.s.y-24) >> 4, p.z >>5)
) {
return true;
}
// Do the normal collision check with the whole box
return map_block_at_box(vec3_sub(p, this.s), vec3_add(p, this.s));
}
_did_collide(axis) {}
_did_collide_with_entity(other) {}
_draw_model() {
this._anim_time += game_tick;
// Calculate which frames to use and how to mix them
let f = (this._anim_time / this._anim[0]),
mix = f - (f|0),
frame_cur = this._anim[1][(f|0) % this._anim[1].length],
frame_next = this._anim[1][((f+1)|0) % this._anim[1].length];
// Swap frames if we're looping to the first frame again
if (frame_next < frame_cur) {
[frame_next, frame_cur] = [frame_cur, frame_next];
mix = 1-mix;
}
r_draw(
this.p, this._yaw, this._pitch, this._texture,
this._model.f[frame_cur], this._model.f[frame_next], mix,
this._model.nv
);
}
_spawn_particles(amount, speed = 1, model, texture, lifetime) {
for (let i = 0; i < amount; i++) {
let particle = game_spawn(entity_particle_t, this.p);
particle._model = model;
particle._texture = texture;
particle._die_at = game_time + lifetime + Math.random() * lifetime * 0.2;
particle.v = vec3(
(Math.random() - 0.5) * speed,
Math.random() * speed,
(Math.random() - 0.5) * speed
);
}
}
_receive_damage(from, amount) {
if (this._dead) {
return;
}
this._health -= amount;
if (this._health <= 0) {
this._kill();
}
}
_play_sound(sound) {
let volume = clamp(scale(vec3_dist(this.p, r_camera), 64, 1200, 1, 0),0,1),
pan = Math.sin(vec3_2d_angle(this.p, r_camera)-r_camera_yaw)*-1;
audio_play(sound, volume, 0, pan);
}
_kill() {
this._dead = 1;
}
}
+30
View File
@@ -0,0 +1,30 @@
class entity_barrel_t extends entity_t {
_init() {
this._model = model_barrel;
this._texture = 21;
this._pitch = Math.PI/2;
this._health = 10;
this.s = vec3(8, 32, 8);
game_entities_enemies.push(this);
}
_kill() {
// Deal some damage to nearby entities
for (let entity of game_entities_enemies) {
let dist = vec3_dist(this.p, entity.p);
if (entity !== this && dist < 256) {
entity._receive_damage(this, scale(dist, 0, 256, 60, 0));
}
}
super._kill();
this._play_sound(sfx_grenade_explode);
for (let m of model_gib_pieces) {
this._spawn_particles(2, 600, m, 21, 1);
}
game_spawn(entity_light_t, vec3_add(this.p, vec3(0,16,0)), 250, 0x08f)._die_at = game_time + 0.2;
game_entities_enemies = game_entities_enemies.filter(e => e != this);
}
}
+44
View File
@@ -0,0 +1,44 @@
class entity_door_t extends entity_t {
_init(texture, dir) {
this._model = model_door;
this._texture = texture;
this._health = 10;
this.s = vec3(64, 64, 64);
this._start_pos = vec3_clone(this.p);
this._reset_state_at = 0;
this._yaw = dir * Math.PI/2;
this._open = 0;
// Map 1 only has one door and it needs a key. Should be a flag
// in the entity data instead :/
this._needs_key = game_map_index == 1;
// Doors block enemies and players
game_entities_enemies.push(this);
game_entities_friendly.push(this);
}
_update() {
this._draw_model();
if (vec3_dist(this.p, game_entity_player.p) < 128) {
if (this._needs_key) {
game_show_message('YOU NEED THE KEY...');
return;
}
this._reset_state_at = game_time + 3;
}
if (this._reset_state_at < game_time) {
this._open = Math.max(0, this._open-game_tick);
}
else {
this._open = Math.min(1, this._open+game_tick);
}
this.p = vec3_add(this._start_pos, vec3_rotate_y(vec3(96 * this._open,0,0), this._yaw));
}
_receive_damage() {}
}
+199
View File
@@ -0,0 +1,199 @@
class entity_enemy_t extends entity_t {
_init(patrol_dir) {
// Animations
this._ANIMS =[
[1, [0]], // 0: Idle
[0.40, [1,2,3,4]], // 1: Walk
[0.20, [1,2,3,4]], // 2: Run
[0.25, [0,5,5,5]], // 3: Attack prepare
[0.25, [5,0,0,0]], // 4: Attack
];
// State definitions
// [0: anim_index, 1: speed, 2: next_state_update, 3: next_state]
this._STATE_IDLE = [0, 0, 0.1];
this._STATE_PATROL = [1, 0.5, 0.5];
this._STATE_FOLLOW = [2, 1, 0.3];
this._STATE_ATTACK_RECOVER = [0, 0, 0.1, this._STATE_FOLLOW];
this._STATE_ATTACK_EXEC = [4, 0, 0.4, this._STATE_ATTACK_RECOVER];
this._STATE_ATTACK_PREPARE = [3, 0, 0.4, this._STATE_ATTACK_EXEC];
this._STATE_ATTACK_AIM = [0, 0, 0.1, this._STATE_ATTACK_PREPARE];
this._STATE_EVADE = [2, 1, 0.8, this._STATE_ATTACK_AIM];
this.s = vec3(12,28,12);
this._step_height = 17;
this._speed = 196;
this._target_yaw = this._yaw;
this._state_update_at = 0;
this._attack_distance = 800;
this._evade_distance = 96;
this._attack_chance = 0.65;
this._keep_off_ledges = 1;
this._turn_bias = 1;
this._check_against = ENTITY_GROUP_PLAYER;
game_entities_enemies.push(this);
// If patrol_dir is non-zero it determines the partrol direction in
// increments of 90°. Otherwise we just idle.
if (patrol_dir) {
this._set_state(this._STATE_PATROL);
this._target_yaw = (Math.PI/2) * patrol_dir;
this._anim_time = Math.random();
}
else {
this._set_state(this._STATE_IDLE);
}
}
_set_state(state) {
this._state = state;
this._anim = this._ANIMS[state[0]];
this._anim_time = 0;
this._state_update_at = game_time + state[2] + state[2]/4 * Math.random();
}
_update() {
// Is it time for a state update?
if (this._state_update_at < game_time) {
// Choose a new turning bias for FOLLOW/EVADE when we hit a wall
this._turn_bias = Math.random() > 0.5 ? 0.5 : -0.5;
let distance_to_player = vec3_dist(this.p, game_entity_player.p),
angle_to_player = vec3_2d_angle(this.p, game_entity_player.p);
if (this._state[3]) {
this._set_state(this._state[3]);
}
// Try to minimize distance to the player
if (this._state == this._STATE_FOLLOW) {
// Do we have a line of sight?
if (!map_trace(this.p, game_entity_player.p)) {
this._target_yaw = angle_to_player;
}
// Are we close enough to attack?
if (distance_to_player < this._attack_distance) {
// Are we too close? Evade!
if (
distance_to_player < this._evade_distance ||
Math.random() > this._attack_chance
) {
this._set_state(this._STATE_EVADE);
this._target_yaw += Math.PI/2 + Math.random() * Math.PI;
}
// Just the right distance to attack!
else {
this._set_state(this._STATE_ATTACK_AIM);
}
}
}
// We just attacked; just keep looking at the player 0_o
if (this._state == this._STATE_ATTACK_RECOVER) {
this._target_yaw = angle_to_player;
}
// Wake up from patroling or idlyng if we have a line of sight
// and are near enough
if (this._state == this._STATE_PATROL || this._state == this._STATE_IDLE) {
if (
distance_to_player < 700 &&
!map_trace(this.p, game_entity_player.p)
) {
this._set_state(this._STATE_ATTACK_AIM);
}
}
// Aiming - reorient the entity towards the player, check
// if we have a line of sight
if (this._state == this._STATE_ATTACK_AIM) {
this._target_yaw = angle_to_player;
// No line of sight? Randomly shuffle around :/
if (map_trace(this.p, game_entity_player.p)) {
this._set_state(this._STATE_EVADE);
}
}
// Execute the attack!
if (this._state == this._STATE_ATTACK_EXEC) {
this._attack();
}
}
// Rotate to desired angle
this._yaw += anglemod(this._target_yaw - this._yaw) * 0.1;
// Move along the yaw direction with the current speed (which might be 0)
if (this._on_ground) {
this.v = vec3_rotate_y(vec3(0, this.v.y, this._state[1] * this._speed), this._target_yaw);
}
this._update_physics();
this._draw_model();
}
_spawn_projectile(type, speed, yaw_offset, pitch_offset) {
let projectile = game_spawn(type, this.p);
projectile._check_against = ENTITY_GROUP_PLAYER;
projectile._yaw = this._yaw + Math.PI/2;
projectile.v = vec3_rotate_yaw_pitch(
vec3(0, 0, speed),
this._yaw + yaw_offset,
Math.atan2(
this.p.y-game_entity_player.p.y,
vec3_dist(this.p, game_entity_player.p)
) + pitch_offset
);
return projectile;
}
_receive_damage(from, amount) {
super._receive_damage(from, amount);
this._play_sound(sfx_enemy_hit);
// Wake up if we're idle or patrolling
if (this._state == this._STATE_IDLE || this._state == this._STATE_PATROL) {
this._target_yaw = vec3_2d_angle(this.p, game_entity_player.p);
this._set_state(this._STATE_FOLLOW);
}
this._spawn_particles(2, 200, model_blood, 18, 0.5);
}
_kill() {
super._kill();
for (let m of model_gib_pieces) {
this._spawn_particles(2, 300, m, 18, 1);
}
this._play_sound(sfx_enemy_gib);
game_entities_enemies = game_entities_enemies.filter(e => e != this);
}
_did_collide(axis) {
if (axis == 1) {
return;
}
// If we hit a wall/ledge while patrolling just turn around 180
if (this._state == this._STATE_PATROL) {
this._target_yaw += Math.PI;
}
else {
this._target_yaw += this._turn_bias;
}
}
}
+15
View File
@@ -0,0 +1,15 @@
class entity_enemy_enforcer_t extends entity_enemy_t {
_init(patrol_dir) {
super._init(patrol_dir);
this._model = model_enforcer;
this._texture = 19;
this._health = 80;
this.s = vec3(14,44,14);
}
_attack() {
this._play_sound(sfx_plasma_shoot);
this._spawn_projectile(entity_projectile_plasma_t, 800, 0, 0);
}
}
+21
View File
@@ -0,0 +1,21 @@
class entity_enemy_grunt_t extends entity_enemy_t {
_init(patrol_dir) {
super._init(patrol_dir);
this._model = model_grunt;
this._texture = 17;
this._health = 40;
}
_attack() {
this._play_sound(sfx_shotgun_shoot);
game_spawn(entity_light_t, vec3_add(this.p, vec3(0,30,0)), 10, 0xff)._die_at = game_time + 0.1;
for (let i = 0; i < 3; i++) {
this._spawn_projectile(
entity_projectile_shell_t, 10000,
Math.random()*0.08-0.04, Math.random()*0.08-0.04
);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
class entity_enemy_hound_t extends entity_enemy_t {
_init(patrol_dir) {
super._init(patrol_dir);
this._model = model_hound;
this._texture = 22;
this._health = 25;
this._check_against = ENTITY_GROUP_PLAYER;
this.s = vec3(12,16,12);
this._attack_distance = 200;
this._evade_distance = 64;
this._attack_chance = 0.7;
this._speed = 256;
this._ANIMS = [
[1, [0]], // 0: Idle
[0.15, [0,1]], // 2: Run
[0.15, [0,1]], // 2: Run
[1, [0]], // 3: Attack prepare
[0.1, [0,1,1,1,0,0,0]], // 4: Attack
];
this._STATE_PATROL = [1, 0.2, 0.5];
this._STATE_ATTACK_RECOVER = [0, 0, 0.5, this._STATE_FOLLOW];
this._STATE_ATTACK_EXEC = [4, 0, 1, this._STATE_ATTACK_RECOVER];
this._STATE_ATTACK_PREPARE = [3, 0, 0.0, this._STATE_ATTACK_EXEC];
this._STATE_ATTACK_AIM = [0, 0, 0.0, this._STATE_ATTACK_PREPARE];
this._STATE_EVADE = [2, 1, 0.3, this._STATE_ATTACK_AIM];
this._set_state(this._STATE_IDLE);
}
_did_collide_with_entity(other) {
if (!this._did_hit && this._state == this._STATE_ATTACK_EXEC) {
this._did_hit = 1;
other._receive_damage(this, 14);
}
}
_attack() {
this._play_sound(sfx_enemy_hound_attack);
this.v = vec3_rotate_y(vec3(0, 250, 600), this._target_yaw);
this._on_ground = 0;
this._did_hit = 0;
// Ignore ledges while attacking
this._keep_off_ledges = 0;
clearTimeout(this._reset_ledges);
this._reset_ledges = setTimeout(()=>this._keep_off_ledges = 1, 1000);
}
}
+25
View File
@@ -0,0 +1,25 @@
class entity_enemy_ogre_t extends entity_enemy_t {
_init(patrol_dir) {
super._init(patrol_dir);
this._model = model_ogre;
this._texture = 20;
this._speed = 96;
this._health = 200;
this.s = vec3(14,36,14);
this._attack_distance = 350;
this._ANIMS =[
[1, [0]], // 0: Idle
[0.80, [1,2,3,4]], // 1: Walk
[0.40, [1,2,3,4]], // 2: Run
[0.35, [0,5,5,5]], // 3: Attack prepare
[0.35, [5,0,0,0]], // 4: Attack
];
}
_attack() {
this._play_sound(sfx_grenade_shoot);
this._spawn_projectile(entity_projectile_grenade_t, 600, 0, -0.4)._damage = 40;
}
}
+34
View File
@@ -0,0 +1,34 @@
class entity_enemy_zombie_t extends entity_enemy_t {
_init(patrol_dir) {
super._init(patrol_dir);
this._model = model_zombie;
this._texture = 18;
this._speed = 0;
this._attack_distance = 350;
this._health = 60;
this._ANIMS[3] = [0.25, [0,0,5,5]]; // 3: Attack prepare
this._STATE_FOLLOW = [0, 0, 0.1];
this._STATE_ATTACK_RECOVER = [0, 0, 1.1, this._STATE_IDLE];
this._STATE_ATTACK_EXEC = [4, 0, 0.4, this._STATE_ATTACK_RECOVER];
this._STATE_ATTACK_PREPARE = [3, 0, 0.4, this._STATE_ATTACK_EXEC];
this._STATE_ATTACK_AIM = [0, 0, 0.1, this._STATE_ATTACK_PREPARE];
this._STATE_EVADE = [0, 0, 0.1, this._STATE_ATTACK_AIM];
this._set_state(this._STATE_IDLE);
}
_receive_damage(from, amount) {
// Ignore damage that's not large enough to gib us
if (amount > 60) {
super._receive_damage(from, amount)
}
}
_attack() {
this._play_sound(sfx_enemy_hit);
this._spawn_projectile(entity_projectile_gib_t, 600, 0, -0.5);
}
}
+35
View File
@@ -0,0 +1,35 @@
class entity_light_t extends entity_t {
_init(light, color) {
this._light = light;
this._spawn_time = game_time;
if (light == 1) {
this._flicker = true;
}
if (!color) {
console.log('no color!')
}
this._color = [
((color & 0x7) << 5),
((color & 0x1c) << 3),
(color & 0xc0)
];
}
_update() {
if (this._flicker && Math.random() > 0.9) {
this._light = Math.random() > 0.5 ? 10 : 0;
}
let intensity = this._light;
// If this light is a temporary one, fade it out over its lifetime
if (this._die_at) {
if (this._die_at < game_time) {
this._kill();
}
intensity = scale(game_time, this._spawn_time, this._die_at, 1, 0) * this._light;
}
r_push_light(this.p, intensity, this._color[0], this._color[1], this._color[2]);
}
}
+14
View File
@@ -0,0 +1,14 @@
class entity_particle_t extends entity_t {
_init() {
this._bounciness = 0.5;
this.f = 0.1;
}
_update() {
this._yaw += this.v.y * 0.001;
this._pitch += this.v.x * 0.001;
this._update_physics();
this._draw_model();
}
}
+18
View File
@@ -0,0 +1,18 @@
class entity_pickup_t extends entity_t {
_init() {
this._model = model_pickup_box;
this.s = vec3(12,12,12);
this._yaw += Math.PI/2;
}
_update() {
if (!this._on_ground) {
this._update_physics();
}
this._draw_model();
if (vec3_dist(this.p, game_entity_player.p) < 40) {
this._pickup();
}
}
}
+19
View File
@@ -0,0 +1,19 @@
class entity_pickup_grenadelauncher_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 21;
this._model = model_pickup_grenadelauncher;
}
_update() {
this._yaw += 0.02;
super._update();
}
_pickup() {
audio_play(sfx_pickup);
game_entity_player._weapon_index = game_entity_player._weapons.push(new weapon_grenadelauncher_t) - 1;
this._kill();
}
}
+18
View File
@@ -0,0 +1,18 @@
class entity_pickup_grenades_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 25;
this._model = model_pickup_grenades;
}
_pickup() {
for (let w of game_entity_player._weapons) {
if (w instanceof(weapon_grenadelauncher_t)) {
w._ammo += 10;
audio_play(sfx_pickup);
this._kill();
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
class entity_pickup_health_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 23;
}
_pickup() {
audio_play(sfx_pickup);
game_entity_player._health += 25;
this._kill();
}
}
+25
View File
@@ -0,0 +1,25 @@
class entity_pickup_key_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 21;
this._model = model_pickup_key;
}
_update() {
this._yaw += 0.02;
super._update();
}
_pickup() {
audio_play(sfx_pickup);
game_show_message('YOU GOT THE KEY!');
for (let e of game_entities) {
if (e._needs_key) {
e._needs_key = 0;
break;
}
}
this._kill();
}
}
+19
View File
@@ -0,0 +1,19 @@
class entity_pickup_nailgun_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 12;
this._model = model_pickup_nailgun;
}
_update() {
this._yaw += 0.02;
super._update();
}
_pickup() {
audio_play(sfx_pickup);
game_entity_player._weapon_index = game_entity_player._weapons.push(new weapon_nailgun_t) - 1;
this._kill();
}
}
+17
View File
@@ -0,0 +1,17 @@
class entity_pickup_nails_t extends entity_pickup_t {
_init() {
super._init();
this._texture = 24;
}
_pickup() {
for (let w of game_entity_player._weapons) {
if (w instanceof(weapon_nailgun_t)) {
w._ammo += 50;
audio_play(sfx_pickup);
this._kill();
}
}
}
}
+125
View File
@@ -0,0 +1,125 @@
class entity_player_t extends entity_t {
_init() {
this.s = vec3(12,24,12);
this.f = 10;
this._speed = 3000;
this._step_height = 17;
this._can_jump = 0;
this._can_shoot_at = 0;
this._health = 100;
this._check_against = ENTITY_GROUP_ENEMY;
this._weapons = [new weapon_shotgun_t];
this._weapon_index = 0;
// Map 1 needs some rotation of the starting look-at direction
this._yaw += game_map_index * Math.PI;
this._bob = 0;
game_entity_player = this;
game_entities_friendly.push(this);
}
_update() {
// Mouse look
this._pitch = clamp(this._pitch + mouse_y * m.value * 0.00015, -1.5, 1.5);
this._yaw = (this._yaw + mouse_x * m.value * 0.00015) % (Math.PI*2);
// Acceleration in movement direction
this.a = vec3_mulf(
vec3_rotate_y(
vec3(
keys[key_right] - keys[key_left],
0,
keys[key_up] - keys[key_down]
),
this._yaw
),
this._speed * (this._on_ground ? 1 : 0.3)
);
if (keys[key_jump] && this._on_ground && this._can_jump) {
this.v.y = 400;
this._on_ground = 0;
this._can_jump = 0;
}
if (!keys[key_jump]) {
this._can_jump = 1;
}
this._weapon_index = (
this._weapon_index + keys[key_next] + this._weapons.length - keys[key_prev]
) % this._weapons.length;
let shoot_wait = this._can_shoot_at - game_time,
weapon = this._weapons[this._weapon_index];
// Shoot Weapon
if (keys[key_action] && shoot_wait < 0) {
this._can_shoot_at = game_time + weapon._reload;
if (weapon._needs_ammo && weapon._ammo == 0) {
audio_play(sfx_no_ammo);
}
else {
weapon._shoot(this.p, this._yaw, this._pitch);
game_spawn(entity_light_t, this.p, 10, 0xff)._die_at = game_time + 0.1;
}
}
this._bob += vec3_length(this.a) * 0.0001;
this.f = this._on_ground ? 10 : 2.5;
this._update_physics();
r_camera.x = this.p.x;
r_camera.z = this.p.z;
// Smooth step up on stairs
r_camera.y = this.p.y + 8 - clamp(game_time - this._stepped_up_at, 0, 0.1) * -160;
r_camera_yaw = this._yaw;
r_camera_pitch = this._pitch;
// Draw weapon at camera position at an offset and add the current
// recoil (calculated from shoot_wait and weapon._reload) accounting
// for the current view yaw/pitch
r_draw(
vec3_add(
r_camera,
vec3_rotate_yaw_pitch(
vec3(
0,
-10 + Math.sin(this._bob)*0.3,
12 + clamp(scale(shoot_wait, 0, weapon._reload, 5, 0), 0, 5)
),
this._yaw, this._pitch
)
),
this._yaw + Math.PI/2, this._pitch,
weapon._texture, weapon._model.f[0], weapon._model.f[0], 0,
weapon._model.nv
);
h.textContent = this._health|0;
a.textContent = weapon._needs_ammo ? weapon._ammo : '∞';
// Debug: a light around the player
// r_push_light(vec3_add(this.p, vec3(0,64,0)), 10, 255, 192, 32);
}
_receive_damage(from, amount) {
audio_play(sfx_hurt);
super._receive_damage(from, amount);
}
_kill() {
super._kill();
h.textContent = this._health|0;
title_show_message('YOU DIED');
setTimeout(() => game_init(game_map_index), 2000);
}
}
+29
View File
@@ -0,0 +1,29 @@
class entity_projectile_gib_t extends entity_t {
_init() {
this._texture = 18;
this._bounciness = 0;
this._die_at = game_time + 2;
this._model = model_gib;
this._yaw = Math.random();
this._pitch = Math.random();
}
_update() {
super._update_physics();
this._draw_model();
this.f = this._on_ground ? 15 : 0;
}
_did_collide(axis) {
if (axis == 1 && this.v.y < -128) {
this._play_sound(sfx_enemy_hit);
}
}
_did_collide_with_entity(other) {
other._receive_damage(this, 10);
this._kill();
}
}
+43
View File
@@ -0,0 +1,43 @@
class entity_projectile_grenade_t extends entity_t {
_init() {
this._texture = 8;
this._model = model_grenade;
this._die_at = game_time + 2;
this._bounciness = 0.5;
this._damage = 120;
}
_update() {
super._update_physics();
this._draw_model();
r_push_light(vec3_add(this.p, vec3(0,16,0)), (Math.sin(game_time*10)+2)*0.5, 255, 32, 0);
this.f = this._on_ground ? 5 : 0.5;
}
_did_collide(axis) {
if (axis != 1 || this.v.y < -128) {
this._yaw += Math.random();
this._play_sound(sfx_grenade_bounce);
}
}
_did_collide_with_entity(other) {
this._kill();
}
_kill() {
// Deal some damage to nearby entities
for (let entity of this._check_entities) {
let dist = vec3_dist(this.p, entity.p);
if (dist < 196) {
entity._receive_damage(this, scale(dist, 0, 196, this._damage, 0));
}
}
super._kill();
this._play_sound(sfx_grenade_explode);
this._spawn_particles(20, 800, model_explosion, 8, 1);
game_spawn(entity_light_t, vec3_add(this.p, vec3(0,16,0)), 250, 0x08f)._die_at = game_time + 0.2;
}
}
+26
View File
@@ -0,0 +1,26 @@
class entity_projectile_nail_t extends entity_t {
_init() {
this._texture = 2;
this._model = model_nail;
this._gravity = 0;
this._die_at = game_time + 3;
}
_update() {
this._update_physics();
this._draw_model();
}
_did_collide(axis) {
this._kill();
this._play_sound(sfx_nailgun_hit);
this._spawn_particles(2, 80, model_explosion, 8, 0.4);
game_spawn(entity_light_t, this.p, 1, 0xff)._die_at = game_time + 0.1;
}
_did_collide_with_entity(other) {
this._kill();
other._receive_damage(this, 9);
}
}
+27
View File
@@ -0,0 +1,27 @@
class entity_projectile_plasma_t extends entity_t {
_init() {
this._texture = 21;
this._model = model_nail;
this._gravity = 0;
this._die_at = game_time + 3;
}
_update() {
this._update_physics();
this._draw_model();
r_push_light(this.p, 5, 255, 128, 0);
}
_did_collide(axis) {
this._kill();
this._play_sound(sfx_nailgun_hit);
this._spawn_particles(2, 80, model_explosion, 8, 0.4);
game_spawn(entity_light_t, vec3_add(this.p, vec3(0,10,0)), 5, 0xf5)._die_at = game_time + 0.1;
}
_did_collide_with_entity(other) {
this._kill();
other._receive_damage(this, 15);
}
}
+22
View File
@@ -0,0 +1,22 @@
class entity_projectile_shell_t extends entity_t {
_init() {
this._gravity = 0;
this._die_at = game_time + 0.1;
}
_update() {
this._update_physics();
}
_did_collide(axis) {
this._kill();
this._spawn_particles(2, 80, model_explosion, 4, 0.4);
game_spawn(entity_light_t, this.p, 0.5, 0xff)._die_at = game_time + 0.1;
}
_did_collide_with_entity(other) {
this._kill();
other._receive_damage(this, 4);
}
}
+35
View File
@@ -0,0 +1,35 @@
class entity_torch_t extends entity_t {
_init() {
this._texture = 30;
this._model = model_torch;
this._anim = [0.05, [0,1,2,1,2,0,0,1,2]];
this.p.x -= 16;
this.p.z -= 16;
this._light_pos = this.p;
// Find which wall we're on; move to the torch model towards the wall
// and the light position outwards
for (let trace_dir of [vec3(-32,0,0), vec3(32,0,0), vec3(0,0,-32), vec3(0,0,32)]) {
let trace_end = vec3_add(this.p, trace_dir);
if (map_trace(this.p, vec3_add(this.p, trace_dir))) {
this.p = vec3_add(this.p, vec3_mulf(trace_dir, 0.4));
this._light_pos = vec3_sub(this.p, vec3_mulf(trace_dir, 2));
break;
}
}
this._light = 0;
}
_update() {
super._update();
if (Math.random() > 0.8) {
this._light = Math.random();
}
r_push_light(this._light_pos, Math.sin(game_time)+this._light+6, 255,192,16);
}
}
+9
View File
@@ -0,0 +1,9 @@
class entity_trigger_level_t extends entity_t {
_update() {
if (!this._dead && vec3_dist(this.p, game_entity_player.p) < 64) {
game_next_level();
this._dead = 1;
}
}
}
Executable
+92
View File
@@ -0,0 +1,92 @@
let
game_tick = 0,
game_time = 0.016,
game_real_time_last,
game_message_timeout = 0,
game_entities,
game_entities_enemies,
game_entities_friendly,
game_entity_player,
game_map_index,
game_jump_to_next_level,
game_init = (map_index) => {
ts.style.display = 'none',
game_entities = [];
game_entities_enemies = [];
game_entities_friendly = [];
game_map_index = map_index;
map_init(map_data[game_map_index]);
},
game_next_level = () => {
game_jump_to_next_level = 1;
},
game_spawn = (type, pos, p1, p2) => {
let entity = new (type)(pos, p1, p2)
game_entities.push(entity);
return entity;
},
game_show_message = (text) => {
msg.textContent = text;
msg.style.display = 'block';
clearTimeout(game_message_timeout);
game_message_timeout = setTimeout(()=>msg.style.display = 'none', 2000);
},
title_show_message = (msg, sub = '') => {
ts.innerHTML = '<h1>'+msg+'</h1>' + sub;
ts.style.display = 'block';
},
game_run = (time_now) => {
requestAnimationFrame(game_run);
time_now *= 0.001;
game_tick = Math.min((time_now - (game_real_time_last||time_now)),0.05);
game_real_time_last = time_now;
game_time += game_tick;
r_prepare_frame(0.1, 0.2, 0.5);
// Update and render entities
let alive_entities = [];
for (let entity of game_entities) {
if (!entity._dead) {
entity._update();
alive_entities.push(entity);
}
}
game_entities = alive_entities;
map_draw();
r_end_frame();
// Reset mouse movement and buttons that should be pressed, not held.
mouse_x = mouse_y = 0;
keys[key_next] = keys[key_prev] = 0;
if (game_jump_to_next_level) {
game_jump_to_next_level = 0;
game_map_index++;
if (game_map_index == 2) {
title_show_message('THE END', 'THANKS FOR PLAYING ❤');
h.textContent = a.textContent = '';
game_entity_player._dead = 1;
// Set camera position for end screen
r_camera = vec3(1856,784,2272);
r_camera_yaw = 0;
r_camera_pitch = 0.5;
}
else {
game_init(game_map_index);
}
}
};
+3
View File
@@ -0,0 +1,3 @@
<!doctype html><html><head><title>Q1K3</title></head><body><script>
GAME_SOURCE
</script>
+83
View File
@@ -0,0 +1,83 @@
// We use the ev.code for keyboard input. This contains a string like "KeyW"
// or "ArrowLeft", which is awkward to use, but it's keyboard layout neutral,
// so that WASD should work with any layout.
// We detect the 6th or 3rd char for each of those strings and map them to an
// in-game button.
// Movement, Action, Prev/Next, Jump
let keymap = {
W: 1, p: 1, // Key[W] or ArrowU[p]
A: 2, e: 2, // Key[A] or ArrowL[e]ft
S: 3, o: 3, // Key[S] or ArrowR[i]ght
D: 4, i: 4, // Key[D] or ArrowD[o]wn
Q: 5, // Key[Q]
E: 6, // Key[E]
c: 9, // KeySpa[c]e
},
keys = [
// Unused zeroth key, so we can test the keymap result for truthiness
0,
// WASD/Arrow Keys and prev next have to be set to zero, because we use
// the value (0 or 1) to calculate the move direction or weapon switch
0,0,0,0,
0,0
// Following Keys (action, jump) to not have to be set here
// as they are just tested for truthiness
],
key_up = 1,
key_down = 3,
key_left = 2,
key_right = 4,
key_prev = 5,
key_next = 6,
key_action = 7, // ev.button = 0
key_jump = 9, // ev.button = 2
mouse_x = 0,
mouse_y = 0,
last_wheel_event = 0;
document.onkeydown = (ev) => {
let k = keymap[ev.code[6] || ev.code[3]];
if (k) {
ev.preventDefault();
keys[k] = 1;
}
};
document.onkeyup = (ev) => {
let k = keymap[ev.code[6] || ev.code[3]];
if (k) {
ev.preventDefault();
keys[k] = 0;
}
};
document.onwheel = (ev) => {
// Allow for one wheel event every 0.1s. This sucks, but prevents free
// spinning or touch scrolling mouses (eg. Apple Magic Mouse) from doing
// wild things.
if (game_time - last_wheel_event > 0.1) {
keys[key_prev + (ev.deltaY > 1 ? 1 : 0)] = 1;
last_wheel_event = game_time;
}
};
c.onmousemove = (ev) => {
mouse_x += ev.movementX;
mouse_y += ev.movementY;
};
c.onmousedown = (ev) => {
ev.preventDefault();
keys[key_action + ev.button] = 1;
};
c.onmouseup = (ev) => {
ev.preventDefault();
keys[key_action + ev.button] = 0;
};
Executable
+189
View File
@@ -0,0 +1,189 @@
let
map_data,
model_data,
// Particles
model_explosion,
model_blood,
model_gib,
model_gib_pieces = [],
// Enemies
model_grunt,
model_enforcer,
model_ogre,
model_zombie,
model_hound,
// Map Objects
model_barrel,
model_torch,
// Weapon view models
model_shotgun,
model_nailgun,
model_grenadelauncher,
// Pickups
model_pickup_nailgun,
model_pickup_grenadelauncher,
model_pickup_box,
model_pickup_grenades,
model_pickup_key,
model_door,
// Projectiles
model_grenade,
model_plasma, // aka. nail
// Sounds
sfx_enemy_hit,
sfx_enemy_gib,
sfx_enemy_hound_attack,
sfx_no_ammo,
sfx_hurt,
sfx_plasma_shoot,
sfx_shotgun_shoot,
sfx_shotgun_reload,
sfx_nailgun_shoot,
sfx_nailgun_hit,
sfx_grenade_shoot,
sfx_grenade_bounce,
sfx_grenade_explode,
game_load = async () => {
r_init();
// Create textures
ttt(texture_data).map(r_create_texture);
// Load map & model containers
map_data = await map_load_container(/*DEBUG[*/ 'build/' + /*]*/ 'l');
model_data = await model_load_container(/*DEBUG[*/ 'build/' + /*]*/ 'm');
// Create models. Many models share the same geometry just with different
// sizes and textures.
// 0: generic blob
// 1: humanoid
// 2: barrel
// 3: q logo
// 4: hound
// 5: box
// 6: nailgun
// 7: torch
model_q = model_init(model_data[3]);
model_explosion = model_init(model_data[0], 0.1,0.1,0.1);
model_blood = model_init(model_data[0], 0.1,0.2,0.1);
model_gib = model_init(model_data[0], 0.3,0.6,0.3);
model_grunt = model_init(model_data[1], 2.5,2.2,2.5);
model_enforcer = model_init(model_data[1], 3,2.7,3);
model_zombie = model_init(model_data[1], 1.5,2,1.5);
model_ogre = model_init(model_data[1], 4,3,4);
model_hound = model_init(model_data[4],2.5,2.5,2.5);
model_barrel = model_init(model_data[2], 2, 2, 2);
model_torch = model_init(model_data[7], 0.6,1,0.6);
model_pickup_nailgun = model_init(model_data[6], 1, 1, 1);
model_pickup_grenadelauncher = model_init(model_data[2], 1, 0.5, 0.5);
model_pickup_box = model_init(model_data[5], 0.7, 0.7, 0.7);
model_pickup_grenades = model_init(model_data[5], 0.5, 1, 0.5);
model_pickup_key = model_init(model_data[5], 0.1, 0.7, 0.1);
model_door = model_init(model_data[5], 5, 5, 0.5);
model_shotgun = model_init(model_data[2], 1,0.2,0.2);
model_grenadelauncher = model_init(model_data[2], 0.7,0.4,0.4);
model_nailgun = model_init(model_data[6], 0.7,0.7,0.7);
model_grenade = model_init(model_data[2], 0.3,0.3,0.3);
model_nail = model_init(model_data[2], 0.5,0.1,0.1);
// Take some parts from the grunt model and build individual giblet models
// from it. Arms and legs and stuff...
for (let i = 0; i < 204; i+=34) {
let m = model_init(model_data[1], 2,1,2);
m.f[0] += i;
m.nv = 34;
model_gib_pieces.push(m);
}
r_submit_buffer();
requestAnimationFrame(run_frame);
f.onclick = () => g.requestFullscreen();
g.onclick = () => {
g.onclick = () => c.requestPointerLock();
g.onclick();
// title_show_message('...');
audio_init();
setTimeout(()=> {
// Generate sounds
sfx_enemy_hit = audio_create_sound(135, [8,0,0,1,148,1,3,5,0,0,139,1,0,2653,0,2193,255,2,639,119,2,23,0,0,0,0,0,0,0]);
sfx_enemy_gib = audio_create_sound(140, [7,0,0,1,148,1,7,5,0,1,139,1,0,4611,789,15986,195,2,849,119,3,60,0,0,0,1,10,176,1]);
sfx_enemy_hound_attack = audio_create_sound(132, [8,0,0,1,192,1,8,0,0,1,120,1,0,5614,0,20400,192,1,329,252,1,55,0,0,1,1,8,192,3]);
sfx_no_ammo = audio_create_sound(120, [8,0,0,0,96,1,8,0,0,0,0,0,255,0,0,1075,232,1,2132,255,0,0,0,0,0,0,0,0,0]);
sfx_hurt = audio_create_sound(135, [7,3,140,1,232,3,8,0,9,1,139,3,0,4611,1403,34215,256,4,1316,255,0,0,0,1,0,1,7,255,0]);
sfx_pickup = audio_create_sound(140, [7,0,0,1,187,3,8,0,0,1,204,3,0,4298,927,1403,255,0,0,0,3,35,0,0,0,0,0,0,0]);
sfx_plasma_shoot = audio_create_sound(135, [8,0,0,1,147,1,6,0,0,1,159,1,0,197,1234,21759,232,2,2902,255,2,53,0,0,0,0,0,0,0]);
sfx_shotgun_shoot = audio_create_sound(135, [7,3,0,1,255,1,6,0,0,1,255,1,112,548,1979,11601,255,2,2902,176,2,77,0,0,1,0,10,255,1]);
sfx_shotgun_reload = audio_create_sound(125, [9,0,0,1,131,1,0,0,0,0,0,3,255,137,22,1776,255,2,4498,176,2,36,2,84,0,0,3,96,0]);
sfx_nailgun_shoot = audio_create_sound(130, [7,0,0,1,132,1,8,4,0,1,132,2,162,0,0,8339,232,2,2844,195,2,40,0,0,0,0,0,0,0]);
sfx_nailgun_hit = audio_create_sound(135, [8,0,0,1,148,1,0,0,0,0,0,1,255,0,0,2193,128,2,6982,119,2,23,0,0,0,0,0,0,0]);
sfx_grenade_shoot = audio_create_sound(127, [8,0,0,1,171,1,9,3,0,1,84,3,96,2653,0,13163,159,2,3206,255,2,64,0,0,0,1,9,226,0]);
sfx_grenade_bounce = audio_create_sound(168, [7,0,124,0,128,0,8,5,127,0,128,0,125,88,0,2193,125,1,1238,240,1,91,3,47,0,0,0,0,0]);
sfx_grenade_explode = audio_create_sound(135, [8,0,0,1,195,1,6,0,0,1,127,1,255,197,1234,21759,232,2,1052,255,4,73,3,25,1,0,10,227,1]);
audio_play(audio_create_song(...music_data), 1, 1);
game_init(0);
run_frame = game_run;
},100); // Not sure why such a long delay is needed. Ideally we would
// want to use requestAnimationFrame, but the previous DOM
// update (...) never makes it to the screen then ;/
};
},
run_frame = (time_now) => {
r_prepare_frame();
r_draw(
vec3(0,0,0), 0, 0, 1,
model_q.f[0], model_q.f[0], 0,
model_q.nv
);
r_push_light(
vec3(Math.sin(time_now*0.00033)*200, 100, -100),
10, 255,192,32
);
r_push_light(
vec3_rotate_y(vec3(0, 0, 100),time_now*0.00063),
10, 32,64,255
);
r_push_light(
vec3_rotate_y(vec3(100, 0, 0),time_now*0.00053),
10, 196,128,255
);
r_end_frame();
requestAnimationFrame(run_frame);
};
game_load();
Executable
+170
View File
@@ -0,0 +1,170 @@
let
map,
map_size = 128,
map_load_container = async (path) => {
/* Parse map container format
typedef struct {
u8 x, y, z;
u8 sx, sy, sz;
} block_t;
typedef struct {
u8 sentinel;
u8 tex;
} block_texture_t;
typedef struct {
char type;
u8 x, y, z;
u8 data1, data2;
} entity_t;
struct {
u16 blocks_size;
block_t blocks[];
u16 num_entities;
entity_t entities[num_entities];
} map_data;
Block data is interleaved with the block_texture_t struct to denote
the texture index to use for the following blocks.
*/
let data = new Uint8Array(await (await fetch(path)).arrayBuffer()),
maps = [];
for (let i = 0; i < data.length;) {
let blocks_size = data[i++] | (data[i++] << 8),
cm = new Uint8Array(map_size * map_size * map_size >> 3), // collision map
nm = new Uint8Array(map_size * map_size * map_size >> 3), // nav map
b = data.subarray(i, i += blocks_size),
r = [],
t;
// Parse block data and construct geometry and the collision map
for (let j = 0; j < b.length;) {
// First value is either the x coordinate or a texture change
// sentinel value (255) followed by the texture coordinate
if (b[j] == 255) {
j++;
t = b[j++];
}
let
x = b[j++], y = b[j++], z = b[j++],
sx = b[j++], sy = b[j++], sz = b[j++];
// Submit the block to the render buffer; we get the vertex offset
// of this block within the buffer back, so we can draw it later
r.push({
t,
b: r_push_block(
x << 5, y << 4, z << 5,
sx << 5, sy << 4, sz << 5,
t
)
});
// The collision map is a bitmap; 8 x blocks per byte
for (let cz = z; cz < z + sz; cz++) {
for (let cy = y; cy < y + sy; cy++) {
for (let cx = x; cx < x + sx; cx++) {
cm[
(
cz * map_size * map_size +
cy * map_size +
cx
) >> 3
] |= 1 << (cx & 7);
}
}
}
}
// Slice of entity data; we parse it when we actually spawn
// the entities in map_init()
let num_entities = data[i++] | (data[i++] << 8),
e = data.subarray(i, i += num_entities * 6 /*sizeof(entity_t)*/);
maps.push({cm, e, r});
}
return maps;
},
map_init = (m) => {
map = m;
// Entity Id to class - must be consistent with map_packer.c line ~900
let spawn_class = [
/* 00 */ entity_player_t,
/* 01 */ entity_enemy_grunt_t,
/* 02 */ entity_enemy_enforcer_t,
/* 03 */ entity_enemy_ogre_t,
/* 04 */ entity_enemy_zombie_t,
/* 05 */ entity_enemy_hound_t,
/* 06 */ entity_pickup_nailgun_t,
/* 07 */ entity_pickup_grenadelauncher_t,
/* 08 */ entity_pickup_health_t,
/* 09 */ entity_pickup_nails_t,
/* 10 */ entity_pickup_grenades_t,
/* 11 */ entity_barrel_t,
/* 12 */ entity_light_t,
/* 13 */ entity_trigger_level_t,
/* 14 */ entity_door_t,
/* 15 */ entity_pickup_key_t,
/* 16 */ entity_torch_t,
];
// Parse entity data and spawn all entities for this map
for (let i = 0; i < map.e.length;) {
let type = spawn_class[m.e[i++]];
game_spawn(
type,
vec3(m.e[i++] * 32, m.e[i++] * 16, m.e[i++] * 32),
m.e[i++], m.e[i++]
);
}
},
map_block_at = (x, y, z) =>
map.cm[
(
z * map_size * map_size +
y * map_size +
x
) >> 3
] & (1 << (x & 7)),
map_trace = (a, b) => {
let diff = vec3_sub(b, a),
step_dir = vec3_mulf(vec3_normalize(diff), 16),
steps = vec3_length(diff)/16;
for (let i = 0; i < steps; i++) {
a = vec3_add(a, step_dir);
if (map_block_at(a.x >> 5, a.y >> 4, a.z >> 5)) {
return a;
}
}
return null;
},
map_block_at_box = (box_start, box_end) => {
for (let z = box_start.z >> 5; z <= box_end.z >> 5; z++) {
for (let y = box_start.y >> 4; y <= box_end.y >> 4; y++) {
for (let x = box_start.x >> 5; x <= box_end.x >> 5; x++) {
if (map_block_at(x, y, z)) {
return true;
}
}
}
}
return false;
},
map_draw = () => {
let p = vec3();
for (let r of map.r) {
r_draw(p, 0, 0, r.t, r.b,r.b,0,36);
}
};
+21
View File
@@ -0,0 +1,21 @@
let
clamp = (v, min, max) => v < min ? min : (v > max ? max : v),
scale = (v, in_min, in_max, out_min, out_max) => out_min + ((out_max) - out_min) * (((v) - in_min) / ((in_max) - in_min)),
anglemod = (r) => Math.atan2(Math.sin(r), Math.cos(r)),
vec3 = (x = 0, y = 0, z = 0) => ({x, y, z}),
vec3_rotate_yaw_pitch = (p, yaw, pitch) => vec3_rotate_y(vec3_rotate_x(p, pitch), yaw),
vec3_rotate_y = (p, rad) => vec3(p.z * Math.sin(rad) + p.x * Math.cos(rad), p.y, p.z * Math.cos(rad) - p.x * Math.sin(rad)),
vec3_rotate_x = (p, rad) => vec3(p.x, p.y * Math.cos(rad) - p.z * Math.sin(rad), p.y * Math.sin(rad) + p.z * Math.cos(rad)),
vec3_2d_angle = (a, b) => Math.atan2(b.x - a.x, b.z - a.z),
vec3_clone = (a) => vec3(a.x,a.y,a.z),
vec3_length = (a) => Math.hypot(a.x,a.y,a.z),
vec3_dist = (a, b) => vec3_length(vec3_sub(a, b)),
vec3_dot = (a, b) => (a.x * b.x + a.y * b.y + a.z * b.z),
vec3_add = (a, b) => vec3(a.x + b.x, a.y + b.y, a.z + b.z),
vec3_sub = (a, b) => vec3(a.x - b.x, a.y - b.y, a.z - b.z),
vec3_mul = (a, b) => vec3(a.x * b.x, a.y * b.y, a.z * b.z),
vec3_mulf = (a, b) => vec3(a.x * b, a.y * b, a.z * b),
vec3_cross = (a, b) => vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x),
vec3_normalize = (v) => vec3_mulf(v, 1/vec3_length(v)),
vec3_face_normal = (v0, v1, v2) => vec3_normalize(vec3_cross(vec3_sub(v0, v1), vec3_sub(v2, v1)));
+107
View File
@@ -0,0 +1,107 @@
let
model_load_container = async (path) => {
/* Parse Retarded Model Format (.rmf):
struct {
u8 num_frames;
u8 num_verts; // per frame
u8 num_indices;
struct {
u8 x, y, z;
} verts[num_frames * num_verts];
struct {
u8 a_address_inc, b_index, c_index;
} indices[num_indices];
} rmf_data;
*/
let data = new Uint8Array(await (await fetch(path)).arrayBuffer()),
models = [];
for (let i = 0; i < data.length;) {
// let model_size = num_frames * num_verts * 3 + num_indices * 3
let model_size = (data[i++] * data[i++] + data[i++]) * 3;
models.push(data.subarray(i-3, i += model_size));
}
return models;
},
model_init = (data, sx = 1, sy = 1, sz = 1) => {
// Load header, prepare buffers
let j = 0,
num_frames = data[j++],
num_vertices = data[j++],
num_indices = data[j++],
vertices = new Float32Array(num_vertices * num_frames * 3),
indices = new Uint8Array(num_indices * 3),
index_increment = 0,
offset = 2,
// Load vertices, center on origin (-15), scale, find the
// min/max x and y to compute our UV coords accordingly.
min_x = 16,
max_x = -16,
min_y = 16,
max_y = -16;
for (let i = 0; i < num_vertices * num_frames * 3; i += 3) {
vertices[i] = (data[j++] - 15) * sx;
vertices[i+1] = (data[j++] - 15) * sy;
vertices[i+2] = (data[j++] - 15) * sz;
// Find min/max only for the first frame
if (i < num_vertices * 3) {
min_x = Math.min(min_x, vertices[i]);
max_x = Math.max(max_x, vertices[i]);
min_y = Math.min(min_y, vertices[i+1]);
max_y = Math.max(max_y, vertices[i+1]);
}
}
// Load indices, 1x 2bit increment, 2x 7bit absolute
for (let i = 0; i < num_indices * 3; i += 3) {
index_increment += data[j++];
indices[i] = index_increment;
indices[i+1] = data[j++];
indices[i+2] = data[j++];
}
// UV coords in texture space and width/height as fraction of model size
let uf = 1 / (max_x - min_x),
u = -min_x * uf,
vf = -1 / (max_y - min_y),
v = max_y * vf;
// Compute normals for each frame and face and submit to render buffer.
// Capture the current vertex offset for the first vertex of each frame.
let frames = [];
for (let frame_index = 0; frame_index < num_frames; frame_index++) {
frames.push(r_num_verts);
let vertex_offset = frame_index * num_vertices * 3;
for (let i = 0; i < num_indices * 3; i += 3) {
let mv = [], uv = [];
for (let face_vertex = 0, o = 0; face_vertex < 3; face_vertex++) {
let idx = indices[i + face_vertex] * 3;
mv[face_vertex] = vec3(
vertices[vertex_offset + idx + 0],
vertices[vertex_offset + idx + 1],
vertices[vertex_offset + idx + 2]
);
uv[face_vertex] = {
u: vertices[idx + 0] * uf + u,
v: vertices[idx + 1] * vf + v
};
}
let n = vec3_face_normal(mv[2], mv[1], mv[0]);
r_push_vert(mv[2], n, uv[2].u, uv[2].v);
r_push_vert(mv[1], n, uv[1].u, uv[1].v);
r_push_vert(mv[0], n, uv[0].u, uv[0].v);
}
}
return {f: frames, nv: num_indices * 3};
}
+2
View File
@@ -0,0 +1,2 @@
let music_data = [6014,21,88,[[[7,0,0,1,255,0,7,0,0,1,255,0,0,100,0,3636,254,2,1199,254,4,71,0,0,0,0,0,0,0],[1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1],[[126,126,0,0,126,0,0,0,0,0,0,0,0,0,0,0,126,126,0,0,126,0,0,0,0,0,0,0,0,0,0,0]]],[[6,0,0,0,255,2,6,0,18,0,255,2,0,100000,56363,100000,199,2,200,254,8,24,0,0,0,0,0,0,0],[0,0,2,2,3,4,2,2,3,5,2,2,3,4,2,2,3,5],[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[133,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]],[[7,0,0,0,87,2,8,0,0,0,16,3,8,0,22,2193,255,3,1162,51,10,182,2,190,0,1,10,96,0],[0,0,0,0,0,0,1,1,1,1,1,1,1,1],[[149,149,0,0,149,0,149,0,149,149,0,0,149,0,149,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]],[[8,0,0,0,65,2,6,0,0,0,243,3,0,200,7505,20000,204,4,6180,81,4,198,0,0,0,0,6,131,0],[0,0,0,0,0,0,0,0,0,0,1,1,2,3,1,1,2,3],[[132,0,0,0,0,0,0,0,133,0,0,0,137,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[132,0,0,0,0,0,0,0,133,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[132,0,0,0,0,0,0,0,133,0,0,0,125,0,0,0,0,0,0,0,125,0,0,0,0,0,0,0,0,0,0,0]]]]];
+354
View File
@@ -0,0 +1,354 @@
let
options = {antialias: false},
gl = (c.getContext('webgl', options) || c.getContext('experimental-webgl', options)),
R_MAX_VERTS = 1024 * 64, // allow 512k verts max
R_MAX_LIGHT_V3 = 64,
// Vertex shader source. This translates the model position & rotation and also
// mixes positions of two buffers for animations.
R_SOURCE_VS =
'precision highp float;' +
// Vertex positions, normals and uv coords for the fragment shader
'varying vec3 vp,vn;' +
'varying vec2 vt;' +
// Input vertex positions & normals and blend vertex positions & normals
'attribute vec3 p,n,p2,n2;' +
// Input UV coords
'attribute vec2 t;' +
// Camera position (x, y, z) and aspect ratio (w)
'uniform vec4 c;' +
// Model position (x, y, z)
'uniform vec3 mp;' +
// Model rotation (yaw, pitch)
'uniform vec2 mr;' +
// Mouse rotation yaw (x), pitch (y)
'uniform vec2 m;' +
// Blend factor between the two vertex positions
'uniform float f;' +
// Generate a rotation Matrix around the x,y,z axis;
// Used for model rotation and camera yaw
'mat4 rx(float r){' +
'return mat4(' +
'1,0,0,0,' +
'0,cos(r),sin(r),0,' +
'0,-sin(r),cos(r),0,' +
'0,0,0,1' +
');' +
'}' +
'mat4 ry(float r){' +
'return mat4(' +
'cos(r),0,-sin(r),0,' +
'0,1,0,0,' +
'sin(r),0,cos(r),0,' +
'0,0,0,1' +
');' +
'}' +
'mat4 rz(float r){' +
'return mat4(' +
'cos(r),sin(r),0,0,' +
'-sin(r),cos(r),0,0,' +
'0,0,1,0,' +
'0,0,0,1' +
');' +
'}' +
'void main(void){' +
// Rotation Matrixes for model rotation
'mat4 '+
'mry=ry(mr.x),' +
'mrz=rz(mr.y);' +
// Mix vertex positions, rotate and add the model position
'vp=(mry*mrz*vec4(mix(p,p2,f),1.)).xyz+mp;' +
// Mix normals
'vn=(mry*mrz*vec4(mix(n,n2,f),1.)).xyz;' +
// UV coords are handed over to the fragment shader as is
'vt=t;' +
// Final vertex position is transformed by the projection matrix,
// rotated around mouse yaw/pitch and offset by the camera position
// We use a FOV of 90, so the matrix[0] and [5] are conveniently 1.
// (1 / Math.tan((90/180) * Math.PI / 2) === 1)
'gl_Position=' +
'mat4(' +
'1,0,0,0,' +
'0,c.w,0,0,' +
'0,0,1,1,' +
'0,0,-2,0' +
')*' + // projection
'rx(-m.y)*ry(-m.x)*' +
'vec4(vp-c.xyz,1.);' +
'}',
// Fragment shader source. Calculates the lighting, does some cheesy gamma
// correction and reduces the colors of the final output.
R_SOURCE_FS =
'precision highp float;' +
// Vertex positions, normals and uv coords
'varying vec3 vp,vn;' +
'varying vec2 vt;' +
'uniform sampler2D s;' +
// Lights [(x,y,z), [r,g,b], ...]
'uniform vec3 l['+R_MAX_LIGHT_V3+'];' +
'void main(void){' +
'gl_FragColor=texture2D(s,vt);' +
// Debug: no textures
// 'gl_FragColor=vec4(1.0,1.0,1.0,1.0);' +
// Calculate all lights
'vec3 vl;' +
'for(int i=0;i<'+R_MAX_LIGHT_V3+';i+=2) {' +
'vl+=' +
// Angle to normal
'max('+
'dot('+
'vn, normalize(l[i]-vp)' +
')' +
',0.)*' +
'(1./pow(length(l[i]-vp),2.))' + // Inverse distance squared
'*l[i+1];' + // Light color/intensity
'}' +
// Debug: full bright lights
// 'vl = vec3(2,2,2);' +
'gl_FragColor.rgb=floor('+
'gl_FragColor.rgb*pow(vl,vec3(0.75))'+ // Light, Gamma
'*16.0+0.5'+
')/16.0;' + // Reduce final output color for some extra dirty looks
'}',
// 8 properties per vert [x,y,z, u,v, nx,ny,nz]
r_buffer = new Float32Array(R_MAX_VERTS*8),
r_num_verts = 0,
// 2 vec3 per light [(x,y,z), [r,g,b], ...]
r_light_buffer = new Float32Array(R_MAX_LIGHT_V3*3),
r_num_lights = 0,
// Uniform locations
r_u_camera,
r_u_lights,
r_u_mouse,
r_u_pos,
r_u_rotation,
r_u_frame_mix,
// Vertex attribute location for mixing
r_va_p2, r_va_n2,
// Texture handles
r_textures = [],
// Camera position
r_camera = vec3(0, 0,-50),
r_camera_pitch = 0.2,
r_camera_yaw = 0,
// We collect all draw calls in an array and draw them all at once at the end
// the frame. This way the lights buffer will be completely filled and we
// only need to set it once for all geometry
r_draw_calls = [],
r_init = () => {
// Create shorthand WebGL function names
// let webglShortFunctionNames = {};
for (let name in gl) {
if (gl[name].length != undefined) {
gl[name.match(/(^..|[A-Z]|\d.|v$)/g).join('')] = gl[name];
// webglShortFunctionNames[name] = 'gl.' +name.match(/(^..|[A-Z]|\d.|v$)/g).join('');
}
}
// console.log(JSON.stringify(webglShortFunctionNames, null, '\t'));
let shader_program = gl.createProgram();
gl.attachShader(shader_program, r_compile_shader(gl.VERTEX_SHADER, R_SOURCE_VS));
gl.attachShader(shader_program, r_compile_shader(gl.FRAGMENT_SHADER, R_SOURCE_FS));
gl.linkProgram(shader_program);
gl.useProgram(shader_program);
r_u_camera = gl.getUniformLocation(shader_program, 'c');
r_u_lights = gl.getUniformLocation(shader_program, 'l');
r_u_mouse = gl.getUniformLocation(shader_program, 'm');
r_u_pos = gl.getUniformLocation(shader_program, 'mp');
r_u_rotation = gl.getUniformLocation(shader_program, 'mr');
r_u_frame_mix = gl.getUniformLocation(shader_program, 'f');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
r_vertex_attrib(shader_program, 'p', 3, 8, 0); // position
r_vertex_attrib(shader_program, 't', 2, 8, 3); // texture coord
r_vertex_attrib(shader_program, 'n', 3, 8, 5); // normals
r_va_p2 = r_vertex_attrib(shader_program, 'p2', 3, 8, 0); // mix position
r_va_n2 = r_vertex_attrib(shader_program, 'n2', 3, 8, 5); // mix normals
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.enable(gl.CULL_FACE);
gl.viewport(0,0,c.width,c.height);
},
r_compile_shader = (shader_type, shader_source) => {
let shader = gl.createShader(shader_type);
gl.shaderSource(shader, shader_source);
gl.compileShader(shader);
// console.log(gl.getShaderInfoLog(shader));
return shader;
},
r_vertex_attrib = (shader_program, attrib_name, count, vertex_size, offset) => {
let location = gl.getAttribLocation(shader_program, attrib_name);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, count, gl.FLOAT, false, vertex_size * 4, offset * 4);
return location;
},
r_create_texture = (c) => {
let t = {t:gl.createTexture(), c};
gl.bindTexture(gl.TEXTURE_2D, t.t);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, c);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.generateMipmap(gl.TEXTURE_2D);
r_textures.push(t);
},
r_prepare_frame = (r,g,b) => {
gl.clearColor(r,g,b,1);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
r_num_lights = 0;
r_light_buffer.fill(0);
},
r_end_frame = () => {
gl.uniform4f(r_u_camera, r_camera.x, r_camera.y, r_camera.z, 16/9);
gl.uniform2f(r_u_mouse, r_camera_yaw, r_camera_pitch);
gl.uniform3fv(r_u_lights, r_light_buffer);
let vo = 0,
last_texture = -1;
for (let c of r_draw_calls) {
// c = [x, y, z, yaw, pitch, texture, offset1, offset2, mix, length]
// Bind new texture only if it changed from the previous one. The map
// is sorted by texture indices, so this helps.
if (last_texture != c[5]) {
last_texture = c[5];
gl.bindTexture(gl.TEXTURE_2D, r_textures[last_texture].t);
}
gl.uniform3f(r_u_pos, c[0], c[1], c[2]);
gl.uniform2f(r_u_rotation, c[3], c[4]);
gl.uniform1f(r_u_frame_mix, c[8]);
// If we have two different frames, calculate the offset from the
// drawArrays call to the mix frame.
// Setting the vertexAttribPointer is quite expensive, so we only
// do this if we have to; i.e. for animated models.
if (vo != (c[7]-c[6])) {
vo = (c[7]-c[6]);
gl.vertexAttribPointer(r_va_p2, 3, gl.FLOAT, false, 8 * 4, vo*8*4);
gl.vertexAttribPointer(r_va_n2, 3, gl.FLOAT, false, 8 * 4, (vo*8+5)*4);
}
gl.drawArrays(gl.TRIANGLES, c[6], c[9]);
}
// Reset draw calls
r_draw_calls = [];
},
r_draw = (pos, yaw, pitch, texture, f1, f2, mix, num_verts) => {
r_draw_calls.push([
pos.x, pos.y, pos.z, yaw, pitch,
texture, f1, f2, mix, num_verts
]);
},
r_submit_buffer = () => {
gl.bufferData(gl.ARRAY_BUFFER, r_buffer.subarray(0, r_num_verts*8), gl.STATIC_DRAW);
},
r_push_vert = (pos, normal, u, v) => {
r_buffer.set([pos.x, pos.y, pos.z, u, v, normal.x, normal.y, normal.z], r_num_verts * 8);
r_num_verts++;
},
r_push_quad = (v0, v1, v2, v3, u, v) => {
let n = vec3_face_normal(v0, v1, v2);
r_push_vert(v0, n, u, 0);
r_push_vert(v1, n, 0, 0);
r_push_vert(v2, n, u, v);
r_push_vert(v3, n, 0, v);
r_push_vert(v2, n, u, v);
r_push_vert(v1, n, 0, 0);
},
r_push_block = (x, y, z, sx, sy, sz, texture) => {
let canvas = r_textures[texture].c,
index = r_num_verts,
tx = sx/canvas.width,
ty = sy/canvas.height,
tz = sz/canvas.width,
// top
v0 = vec3(x, y + sy, z),
v1 = vec3(x + sx, y + sy, z),
v2 = vec3(x, y + sy, z + sz),
v3 = vec3(x + sx, y + sy, z + sz),
// bottom
v4 = vec3(x, y, z + sz),
v5 = vec3(x + sx, y, z + sz),
v6 = vec3(x, y, z),
v7 = vec3(x + sx, y, z);
r_push_quad(v0, v1, v2, v3, tx, tz); // top
r_push_quad(v4, v5, v6, v7, tx, tz); // bottom
r_push_quad(v2, v3, v4, v5, tx, ty); // front
r_push_quad(v1, v0, v7, v6, tx, ty); // back
r_push_quad(v3, v1, v5, v7, tz, ty); // right
r_push_quad(v0, v2, v6, v4, tz, ty); // left
return index;
},
r_push_light = (pos, intensity, r, g, b) => {
// Calculate the distance to the light, fade it out between 768--1024
let fade = clamp(
scale(
vec3_dist(pos, r_camera),
768, 1024, 1, 0
),
0, 1
) * intensity * 10;
if (fade && r_num_lights < R_MAX_LIGHT_V3/2) {
r_light_buffer.set([pos.x, pos.y, pos.z, r*fade, g*fade, b*fade], r_num_lights*6);
r_num_lights++;
}
};
+2
View File
@@ -0,0 +1,2 @@
let texture_data = [[64,64,0,2,3,1.4,2,17176,1.3],[64,64,38751,1,18,4,2,2,27,9,65530,0,7,1,-1,9,13,5,52,8,8,65528,39039,4,0,0,0,64,512,15,4,0,0,0,64,64,14],[64,64,38751,1,18,4,2,2,27,9,65530,0,7,1,-1,0,13,64,52,64,8,65531,39039,4,0,0,0,64,512,15,4,0,0,0,64,64,14],[64,64,13119,4,1,0,0,64,64,15,0,24,11,17,50,65523,2,8],[32,32,21839,1,0,2,10,2,11,4,65528,10,25931,4,0,0,0,32,32,14],[32,32,17487,0,1,1,30,30,65528,11,21580,4,0,0,0,32,32,15],[32,32,30015,4,5,0,0,32,32,15,1,5,4,2,2,22,6,65522,0,8],[32,32,8751,1,1,1,8,4,11,5,65524,15,17487,4,0,0,0,64,64,15],[32,32,13119,4,4,0,0,32,32,15,1,10,3,11,6,25,10,64536,64568,65519],[32,32,8751,1,1,1,3,3,4,4,65524,14,21565,1,1,-1,15,1,16,16,65522,7,0,1,-1,0,1,15,16,6,65521,0,4,4,0,0,0,32,32,15],[32,32,8719,2,63506,1,4,0,0,0,32,32,12],[32,32,21295,4,10,0,-4,32,298,10,2,4372,1],[32,32,8463,1,-1,1,35,1,35,4,65522,10,34399,0,-1,6,34,6,65526,2,34399,2,29479,1,4,6,0,0,32,32,5],[32,32,5535,4,0,0,0,128,64,14],[32,32,8463,1,0,0,3,3,4,4,0,10,65521,0,4,4,23,23,10,64885,21551,3,4,16,13,0,11,"::][::",4,0,4,4,26,26,15],[32,32,8751,1,1,1,8,3,11,5,65524,15,17487,0,9,6,14,13,15,65525,4383,4,0,0,0,64,64,12,3,10,11,20267,0,8,"---"],[32,32,17487,4,5,0,0,32,32,15,1,4,4,3,3,22,22,65523,7,30587],[64,64,38767,2,36875,2.5,1,4,10,15,8,39,59,15,15,8463,1,3,30,14,5,15,6,12813,4367,38671,0,20,1,22,6,13119,10,38671,4,0,0,0,64,64,11],[32,32,40975,2,63308,1.5,2,63751,7.3],[64,64,13119,4,17,0,0,64,64,15,0,0,29,64,64,0,0,89,4,4,21,-6,22,24,15],[32,32,13119,4,9,0,0,32,32,15,4,8,6,-22,21,32,15,4,18,0,0,32,32,4],[64,64,13119,0,0,0,64,64,0,0,64271,3,-1,50,33795,0,32,"XXX",4,7,0,0,64,64,6],[64,64,34063,4,7,0,0,64,64,12,2,12554,1],[32,32,65535,4,12,0,0,32,32,9,3,6,30,61455,0,25,"+"],[32,32,5903,4,12,0,0,32,32,9,3,5,14,65529,0,12,"NIИ"],[32,32,64271,0,12,1,7,30,65528,8,63247,4,7,0,0,32,32,8],[32,32,13119,1,1,1,14,14,16,32,56328,15,26399,1,-7,17,14,14,16,32,56328,8,26159,2,29706,1,4,0,0,0,32,320,14],[32,32,33567,1,1,1,6,30,16,31,65526,15,33823,1,9,-14,6,30,16,32,65526,15,29743,2,55625,1.5,4,0,0,0,32,320,15],[32,32,12559,1,1,1,14,14,16,16,65525,7,21295,0,1,1,14,14,65525,0,34399,0,17,17,14,14,65524,0,34399,2,8,1.5],[32,32,9503,4,11,0,0,32,32,12,1,1,1,6,7,6,8,65521,0,4],[32,32,15,4,18,0,16,32,32,15,4,27,0,-16,32,32,10]];
+111
View File
@@ -0,0 +1,111 @@
/*
TTT Tiny Texture Tumbler
Dominic Szablewski - https://phoboslab.org
-- LICENSE: The MIT License(MIT)
Copyright(c) 2019 Dominic Szablewski
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files(the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Compress with:
// uglifyjs ttt.js --compress --screw-ie8 --mangle toplevel -o ttt.min.js
let
ttt=(td, only_this_index = -1,stack_depth = 0) => {
return td.filter((d,i) => only_this_index < 0 || i == only_this_index).map(d => {
let i = 0,
e = document.createElement('canvas'),
c = e.getContext('2d'),
rgba_from_2byte = (c) =>
'rgba(' + [
((c>>12)&15) * 17,
((c>>8)&15) * 17,
((c>>4)&15) * 17,
(c&15)/15
].join() + ')',
fill_rect = (x, y, w, h, ...colors) =>
colors.map((color, j) => {
c.fillStyle = rgba_from_2byte(color);
c.fillRect(x+[-1,1,0][j], y+[-1,1,0][j], w, h);
})
;
// Set up canvas width and height
e.width = d[i++];
e.height = d[i++];
// Fill with background color
fill_rect(0, 0, e.width, e.height, 0,0, d[i++]);
// Perform all the steps for this texture
while (i < d.length) {
let f = [
// 0 - rectangle: x, y, width, height, top, bottom, fill
(x, y, width, height, top, bottom, fill) => {
fill_rect(x, y, width, height, top, bottom, fill)
},
// 1 - rectangle_multiple: start_x, start_y, width, height,
// inc_x, inc_y, top, bottom, fill
(sx, sy, w, h, inc_x, inc_y, top, bottom, fill) => {
for (let x = sx; x < e.width; x += inc_x) {
for (let y = sy; y < e.height; y += inc_y) {
fill_rect(x, y, w, h, top, bottom, fill);
}
}
},
// 2 - random noise: color, size
(color, size) => {
for (let x = 0; x < e.width; x += size) {
for (let y = 0; y < e.height; y += size) {
// Take the color value (first 3 nibbles) and
// randomize the alpha value (last nibble)
// between 0 and the input alpha.
fill_rect(
x, y, size, size, 0, 0,
(color&0xfff0) + Math.random()*(color&15)
);
}
}
},
// 3 - text: x, y, color, font,size, text
(x, y, color, font, size, text) => {
c.fillStyle = rgba_from_2byte(color);
c.font = size + 'px ' + ['sans-',''][font]+'serif';
c.fillText(text, x, y);
},
// 4 - draw a previous texture
// We limit the stack depth here to not end up in an infinite
// loop by accident
(texture_index, x, y, w, h, alpha) => {
c.globalAlpha = alpha/15;
(
texture_index < td.length && stack_depth < 16 &&
c.drawImage(
ttt(td, texture_index, stack_depth+1)[0],
x, y, w, h
)
);
c.globalAlpha = 1;
}
][d[i++]];
f(...d.slice(i, i+=f.length));
}
return e;
});
};
+86
View File
@@ -0,0 +1,86 @@
class weapon_t {
constructor() {
this._needs_ammo = 1;
this._projectile_offset = vec3(0,0,8);
this._init();
}
_shoot(pos, yaw, pitch) {
if (this._needs_ammo) {
this._ammo--;
}
audio_play(this._sound);
this._spawn_projectile(pos, yaw, pitch);
}
_spawn_projectile(pos, yaw, pitch) {
let projectile = game_spawn(this._projectile_type, vec3_add(
pos,
vec3_add(
vec3(0, 12, 0),
vec3_rotate_yaw_pitch(
this._projectile_offset,
yaw, pitch
)
)
));
// Set the projectile velocity, yaw and pitch
projectile.v = vec3_rotate_yaw_pitch(
vec3(0, 0, this._projectile_speed),
yaw, pitch
);
projectile._yaw = yaw -Math.PI/2;
projectile._pitch = -pitch;
projectile._check_against = ENTITY_GROUP_ENEMY;
// Alternate left/right fire for next projectile (nailgun)
this._projectile_offset.x *= -1;
}
}
class weapon_shotgun_t extends weapon_t {
_init() {
this._texture = 7;
this._model = model_shotgun;
this._sound = sfx_shotgun_shoot;
this._needs_ammo = 0;
this._reload = 0.9;
this._projectile_type = entity_projectile_shell_t;
this._projectile_speed = 10000;
}
_spawn_projectile(pos, yaw, pitch) {
setTimeout(()=>audio_play(sfx_shotgun_reload), 200);
setTimeout(()=>audio_play(sfx_shotgun_reload), 350);
for (let i = 0; i < 8; i++) {
super._spawn_projectile(pos, yaw+Math.random()*0.08-0.04, pitch+Math.random()*0.08-0.04);
}
}
}
class weapon_nailgun_t extends weapon_t {
_init() {
this._texture = 4;
this._model = model_nailgun;
this._sound = sfx_nailgun_shoot;
this._ammo = 100;
this._reload = 0.09;
this._projectile_type = entity_projectile_nail_t;
this._projectile_speed = 1300;
this._projectile_offset = vec3(6,0,8);
}
}
class weapon_grenadelauncher_t extends weapon_t {
_init() {
this._texture = 21;
this._model = model_grenadelauncher;
this._sound = sfx_grenade_shoot;
this._ammo = 10;
this._reload = 0.650;
this._projectile_type = entity_projectile_grenade_t;
this._projectile_speed = 900;
}
}
+2
View File
@@ -0,0 +1,2 @@
};
gs();
+9
View File
@@ -0,0 +1,9 @@
// Holy hell, wtf, bbq! If we don't wrap the whole game in a global function,
// installed on the window scope, everything will be garbage collected in chrome
// in the roadrolled version, even though the requestAnimationFrame should hold
// on to the closure.
// I assumme it's a weird interaction between eval() (used by roadroller) and
// async functions. In any case, it seems to be a bug in v8.
gs = () => {