5c3d6b990e689c6774a6c6ccd9ed4ca3b3a3d31f
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # 3D client for Tinks!
18 app_namespace
"org.nitlanguage.tinks3d"
19 app_version
(1, 0, git_revision
)
22 android_manifest
"""<uses-permission android:name="android.permission.INTERNET" />"""
36 # Maximum distance from the camera to hear events and display explosions
37 private var far_dist2
= 2000.0
39 # Approximate maximum distance from the camera to display features
40 private var features_radius
= 24
46 var models_rock
= new Array[Model].with_items
(
47 new Model("models/Tall_Rock_1_01.obj"),
48 new Model("models/Tall_Rock_2_01.obj"),
49 new Model("models/Tall_Rock_3_01.obj"),
50 new Model("models/Tall_Rock_4_01.obj"))
53 var models_tree
= new Array[Model].with_items
(
54 new Model("models/Oak_Dark_01.obj"),
55 new Model("models/Oak_Green_01.obj"),
56 new Model("models/Large_Oak_Dark_01.obj"),
57 new Model("models/Large_Oak_Green_01.obj"))
59 # Models of the debris left by a destroyed tank
60 var models_debris
= new Array[Model].with_items
(
61 new Model("models/debris0.obj"),
62 new Model("models/debris1.obj"))
64 # Model the health pickup
65 var model_health
= new Model("models/health.obj")
67 # Model of the tank base (without the turret)
68 var model_tank_base
= new Model("models/tank.obj")
70 # Model of the tank turret
71 var model_tank_turret
= new Model("models/tank-turret.obj")
73 # Blast effect on the ground after an explosion
74 private var blast_texture
= new Texture("textures/blast.png")
75 private var blast_material
: TexturedMaterial do
76 var mat
= new TexturedMaterial([1.0]*4, [0.0]*4, [0.0]*4)
77 mat
.ambient_texture
= blast_texture
80 private var blast_model
= new LeafModel(new Plane, blast_material
)
85 # Explosion image for particle effect
86 private var texture_explosion
= new Texture("particles/explosion00.png")
89 var explosion_system
= new ParticleSystem(20, explosion_program
, texture_explosion
)
91 # Explosion image for particle effect
92 private var texture_smoke
= new Texture("particles/blackSmoke12.png")
95 var smoke_system
= new ParticleSystem(200, smoke_program
, texture_smoke
)
101 var turret_fire
= new Sound("sounds/turret_fire.wav")
103 # Turret is ready to fire sound
104 var turret_ready
= new Sound("sounds/turret_ready.mp3")
110 var ground
: Actor is noinit
111 private var ground_texture
= new Texture("textures/fastgras01.png")
113 # All `Feature` with an associated model, to be drawn on screen
114 var features_in_sight
= new Set[Feature]
121 var logo
= new Texture("textures/splash.png")
122 show_splash_screen logo
125 for model
in models
do model
.load
126 for texture
in all_root_textures
do texture
.load
128 # Modify all textures so they have a higher ambient color
129 for model
in models
do
130 for leaf
in model
.leaves
do
131 var mat
= leaf
.material
132 if mat
isa TexturedMaterial then
134 mat
.ambient_color
[0] = mat
.diffuse_color
[0] * mod
135 mat
.ambient_color
[1] = mat
.diffuse_color
[1] * mod
136 mat
.ambient_color
[2] = mat
.diffuse_color
[2] * mod
138 var tex
= mat
.diffuse_texture
139 if tex
!= null then mat
.ambient_texture
= tex
145 # TODO we may need to move this plane if the player goes far from the center
146 var ground_mesh
= new Plane
147 ground_mesh
.repeat_x
= 1000.0
148 ground_mesh
.repeat_y
= 1000.0
150 var ground_material
= new TexturedMaterial(
151 [0.0, 0.1, 0.0, 1.0], [0.4, 0.4, 0.4, 1.0], [0.0]*4)
152 ground_material
.diffuse_texture
= ground_texture
154 var ground_model
= new LeafModel(ground_mesh
, ground_material
)
155 var ground
= new Actor(ground_model
, new Point3d[Float](0.0, 0.0, 0.0))
156 ground
.scale
= 5000.0
162 world_camera
.near
= 0.1
165 glClearColor
(100.0/256.0, 120.0/256.0, 224.0/256.0, 1.0)
167 # Move the sun a bit off right above
168 light
.position
.x
= 1000.0
169 light
.position
.z
= 500.0
171 # Register our two systems
172 particle_systems
.add explosion_system
173 particle_systems
.add smoke_system
175 # Connect to server (or launch one) and assign models to rules
176 var context
= context
177 context
.game
.story
.assign_models
183 var turn
= context
.do_turn
184 for event
in turn
.events
do event
.client_react
186 # Is the player alive?
187 var local_player
= context
.local_player
188 var local_tank
= null
189 if local_player
!= null then local_tank
= local_player
.tank
191 if local_tank
!= null then
192 # Update camera position above the tank
193 var pos
= local_tank
.pos
194 world_camera
.position
.x
= pos
.x
195 world_camera
.position
.z
= pos
.y
196 world_camera
.yaw
= 1.5 * pi
- local_tank
.heading
197 world_camera
.position
.y
= 1.8
201 redef fun accept_event
(event
)
203 # Let `pressed_keys` be populated first
206 var local_player
= context
.local_player
207 var local_tank
= null
208 if local_player
!= null then local_tank
= local_player
.tank
211 if event
isa QuitEvent or (event
isa KeyEvent and event
.name
== "escape") then
215 if event
isa KeyEvent then
217 var direction_change
= ["w", "a", "s", "d"].has
(event
.name
)
218 if direction_change
and local_tank
!= null and local_player
!= null then
219 var forward
= pressed_keys
.has
("w")
220 var backward
= pressed_keys
.has
("s")
221 var left
= pressed_keys
.has
("a")
222 var right
= pressed_keys
.has
("d")
224 # Cancel contradictory commands
225 if forward
and backward
then
230 if left
and right
then
235 # Set movement and direction
239 else if backward
then move
= -0.5
243 ori
= -local_tank
.rule
.max_direction
/2.0
244 else if right
then ori
= local_tank
.rule
.max_direction
/2.0
246 # Activate to invert the orientation on reverse, (for @R4p4Ss)
247 #if backward then ori = -ori
249 # Bonus when only moving or only turning
250 if not forward
and not backward
then ori
*= 2.0
251 if not left
and not right
then move
*= 2.0
254 local_player
.orders
.add
new TankDirectionOrder(local_tank
, ori
, move
)
259 if event
.name
== "space" and local_tank
!= null and event
.is_down
then
260 if local_player
== null then return false
263 var heading
= local_tank
.heading
265 var target
= new Pos(local_tank
.pos
.x
+ dist
*heading
.cos
, local_tank
.pos
.y
+ dist
*heading
.sin
)
266 local_player
.orders
.add
new AimAndFireOrder(local_tank
, target
)
270 # Open fire with a target?
271 if event
isa PointerEvent and event
.pressed
and not event
.is_move
then
272 if local_player
== null then return false
274 var display
= display
275 if display
== null then return false
277 if local_tank
== null then
278 local_player
.orders
.add
new SpawnTankOrder(local_player
)
282 # Compute approximate target
283 var dx
= event
.x
/ display
.width
.to_f
284 dx
= dx
* 2.0 - 1.0 # center of the screen
285 var fovx
= display
.aspect_ratio
* world_camera
.field_of_view_y
287 var heading
= local_tank
.heading
+ dx
* fovx
289 var dy
= event
.y
/ display
.height
.to_f
292 var ty
= dy
* world_camera
.field_of_view_y
293 var dist
= world_camera
.position
.y
/ ty
.tan
/ 1.6
294 if dist
> 200.0 then dist
= 200.0
297 var target
= new Pos(local_tank
.pos
.x
+ dist
*heading
.cos
, local_tank
.pos
.y
+ dist
*heading
.sin
)
298 local_player
.orders
.add
new AimAndFireOrder(local_tank
, target
)
307 # Story and rules (meta game objects)
309 redef class FeatureRule
310 # Models of different alternatives
311 var models
: Array[Model] is noinit
315 # Models of the tank base
316 var base_model
: Model is noinit
318 # Models of the turret
319 var turret_model
: Model is noinit
324 # Assign models from `app` to the corresponding rules
327 tree
.models
= app
.models_tree
328 rock
.models
= app
.models_rock
329 debris
.models
= app
.models_debris
332 tank
.base_model
= app
.model_tank_base
333 tank
.turret_model
= app
.model_tank_turret
336 health
.models
= [app
.model_health
]
344 # Actor representing this feature, if in sight
345 var actor
: nullable Actor = null
347 # Instantiate `actor` and add it to the 3D world
348 fun add_actor_to_scene
350 app
.features_in_sight
.add
self
353 if actor
!= null then
354 # Reuse existing actor
355 if not app
.actors
.has
(actor
) then app
.actors
.add actor
359 # Apply a random model and rotation to new features
360 actor
= new Actor(rule
.models
.rand
,
361 new Point3d[Float](pos
.x
, 0.0, pos
.y
))
362 actor
.rotation
= 2.0*pi
.rand
369 # Remove `actor` from the `actors` list as it will net be used anymore
373 if actor
!= null then
374 app
.actors
.remove actor
381 # Actors representing this tank, both the base and the turret
382 var actors
: Array[Actor] is lazy
do
383 var actors
= new Array[Actor]
384 var actor
= new Actor(app
.model_tank_base
, new Point3d[Float](0.0, 0.0, 0.0))
388 var tank_turret
= new Actor(app
.model_tank_turret
, new Point3d[Float](0.0, 0.0, 0.0))
389 app
.actors
.add tank_turret
390 actors
.add tank_turret
399 private fun client_react
do end
402 redef class FeatureChangeEvent
403 redef fun client_react
405 var old_feature
= old_feature
406 if old_feature
!= null then old_feature
.destroy_actor
408 var feature
= feature
409 if feature
!= null then feature
.add_actor_to_scene
413 redef class ExplosionEvent
414 redef fun client_react
416 for feature
in destroyed_features
do feature
.destroy_actor
419 app
.explosion_system
.add
(new Point3d[Float](pos
.x
, 1.0, pos
.y
), 4096.0, 0.3)
421 app
.explosion_system
.add
(
422 new Point3d[Float](pos
.x
& 1.0, 1.0 & 1.0, pos
.y
& 1.0),
423 2048.0 & 1024.0, 0.3 & 0.1)
426 # Blast mark on the ground
427 var blast
= new Actor(app
.blast_model
, new Point3d[Float](pos
.x
, 0.05 & 0.04, pos
.y
))
429 blast
.rotation
= 2.0*pi
.rand
434 var dt
= 0.2 * s
.to_f
+ 0.1.rand
435 app
.smoke_system
.add
(
436 new Point3d[Float](pos
.x
& 0.2, 0.0, pos
.y
& 0.2),
437 1024.0 & 512.0, 10.0 & 4.0, dt
)
442 redef class OpenFireEvent
443 redef fun client_react
445 if tank
.pos
.dist2_3d
(app
.world_camera
.position
) < app
.far_dist2
then
451 var a
= tank
.turret
.heading
- 0.025 # Correct to center the art
452 var pos
= new Point3d[Float](tank
.pos
.x
+ d
*a
.cos
, 1.25, tank
.pos
.y
+ d
*a
.sin
)
453 app
.explosion_system
.add
(pos
, 0.75*256.0, 0.15)
458 redef class TurretReadyEvent
459 redef fun client_react
461 if tank
.pos
.dist2_3d
(app
.world_camera
.position
) < app
.far_dist2
then
463 app
.turret_ready
.play
468 redef class TankMoveEvent
469 redef fun client_react
472 for actor
in tank
.actors
do
473 actor
.center
.x
= pos
.x
474 actor
.center
.z
= pos
.y
477 tank
.actors
[0].rotation
= tank
.heading
+ pi
478 tank
.actors
[1].rotation
= tank
.turret
.heading
+ pi
480 # Keep going only for the local tank
481 var local_player
= app
.context
.local_player
482 if local_player
!= tank
.player
then return
484 var center
= tank
.pos
485 var d
= app
.features_radius
486 var l
= center
.x
.to_i
- d
487 var r
= center
.x
.to_i
+ d
488 var t
= center
.y
.to_i
- d
489 var b
= center
.y
.to_i
+ d
491 # Remove out of range features
492 for feature
in app
.features_in_sight
.to_a
do
493 var x
= feature
.pos
.x
.to_i
494 var y
= feature
.pos
.y
.to_i
495 if x
< l
or x
> r
or y
< t
or y
> b
then
496 var actor
= feature
.actor
497 app
.actors
.remove actor
498 app
.features_in_sight
.remove feature
502 # Add newly in range features
505 var feature
= app
.context
.game
.world
[x
, y
]
506 if feature
!= null then
507 feature
.add_actor_to_scene
514 redef class TankDeathEvent
515 redef fun client_react
517 for actor
in tank
.actors
do app
.actors
.remove actor
526 # Square of the distance to 3D coordinates `other`
528 # Same as `dist2` but using `other.z` as the Y value
529 # to adapt from the flat plane on X/Y to X/Z.
530 private fun dist2_3d
(other
: Point3d[Numeric]): N
532 var dx
= other
.x
.sub
(x
)
533 var dy
= other
.z
.sub
(y
)
534 var s
= (dx
.mul
(dx
)).add
(dy
.mul
(dy
))
540 # Fuzzy value in `[self-variation..self+variation]`
541 fun &(variation
: Float): Float do return self - variation
+ 2.0*variation
.rand