--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android client with a joystick
+module android_client is
+ app_name "Tinks!"
+ app_namespace "net.xymus.tinks"
+ android_manifest """<uses-permission android:name="android.permission.INTERNET" />"""
+ android_api_target 10
+end
+
+import mnit_android
+import android::audio
+import android::vibration
+import android::landscape
+
+intrude import client
+import controls
+
+redef class App
+
+ # Tank direction control
+ var joystick = new Joystick is lazy
+
+ redef var controls = new Array[Control].with_items(joystick) is lazy
+
+ redef fun input(event)
+ do
+ var local_player = context.local_player
+ var local_tank = local_tank
+ if local_player != null and local_tank != null and event isa ControlEvent then
+ local_player.orders.add new TankDirectionOrder(local_tank, joystick.value_x, joystick.value_y)
+ return true
+ end
+
+ if event isa AndroidKeyEvent then
+ if event.is_back_key then
+ quit = true
+ native_activity.finish
+ return false
+ end
+ end
+
+ return super
+ end
+end
+
+redef class ExplosionEvent
+ redef fun client_react(display, turn)
+ do
+ super
+
+ var local_tank = app.local_tank
+ var d = 20
+ if local_tank != null then
+ d = local_tank.pos.dist(pos).to_i
+ d = 500 - d*20
+ d = d.max(10)
+ end
+
+ app.vibrator.vibrate d
+ end
+end
+
+# On-demand joystick that popups up when tapping the left border of the screen
+class Joystick
+ super Control
+
+ # Current position of the joystick from its center on the X axis, in `[-1.0..1.0]`
+ var value_x = 0.0
+
+ # Current position of the joystick from its center on the Y axis, in `[-1.0..1.0]`
+ var value_y = 0.0
+
+ # Deadzone at the center of the joystick where the values are set at 0.0
+ var deadzone = 0.3
+
+ # Position of the center of the joystick, set at where the screen is first tapped
+ var center: nullable ScreenPos
+
+ # Position of the top of the joystick, the one that follows the finger
+ var handle: nullable ScreenPos
+
+ # Id of the pointer/finger that triggered the joystick
+ var captured_pointer: Int = -1
+
+ # Image of the left border
+ var img_zone: Image = app.assets.drawing.joystick_zone
+
+ # Image of the joystick base
+ var img_back: Image = app.assets.drawing.joystick_back
+
+ # Image of the top of the joystick
+ var img_handle: Image = app.assets.drawing.joystick_handle
+
+ # Radius where the top of the joystick can move around the center
+ var radius: Float = img_back.width.to_f / 2.0 - 4.0
+
+ # Width of the left border used to trigger the joystick
+ var capture_width = 400.0
+
+ redef fun draw(display)
+ do
+ display.blit_stretched(img_zone,
+ 0, -128,
+ 0, display.height+128,
+ capture_width, display.height+128,
+ capture_width, -128)
+
+ var center = center
+ var handle = handle
+ if center != null and handle != null then
+ img_back.scale = 1.0
+ img_handle.scale = 1.0
+ display.blit_centered(img_back, center.x, center.y)
+ display.blit_centered(img_handle, handle.x, handle.y)
+ end
+ end
+
+ redef fun input(event)
+ do
+ if event isa AndroidPointerEvent then
+ var center = center
+ if center == null then
+ # New joystick?
+ print "New joystick? {event.just_went_down} {event.x} < {capture_width}"
+ if event.just_went_down and
+ event.x < capture_width then
+ # Capture it!
+ self.center = new ScreenPos(event.x, event.y)
+ self.captured_pointer = event.pointer_id
+ self.handle = center
+ return true
+ end
+ else
+ # Already down
+
+ # Is it the finger already on the joystick?
+ if captured_pointer != event.pointer_id then return false
+
+ if event.depressed then
+ self.center = null
+ self.handle = null
+ self.value_x = 0.0
+ self.value_y = 0.0
+ else
+ # Update values
+ var dx = center.x - event.x
+ var dy = center.y - event.y # This is inverted, as is the input
+ var d = dx.hypot_with(dy)
+ if d < deadzone then
+ self.value_x = 0.0
+ self.value_y = 0.0
+ self.handle = center
+ else
+ var a = atan2(dx, dy)+pi/2.0
+ self.value_x = a.cos
+ self.value_y = a.sin
+
+ if d > radius then d = radius
+ self.handle = new ScreenPos(center.x+a.cos*d, center.y-a.sin*d)
+ end
+ end
+
+ app.input new ControlEvent(self)
+ return true
+ end
+ end
+
+ return false
+ end
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# On-screen alternative controls
+module controls
+
+import client
+
+redef class App
+
+ # All active UI controls
+ fun controls: Array[Control] is abstract
+
+ redef fun frame_core(display)
+ do
+ super
+
+ for control in controls do control.draw(display)
+ end
+
+ redef fun input(event)
+ do
+ for control in controls do
+ var hit = control.input(event)
+ if hit then return true
+ end
+
+ return super
+ end
+end
+
+# An event raised by a `Control`
+class ControlEvent
+ super InputEvent
+
+ # Sender control
+ var sender: Control
+end
+
+# UI control
+abstract class Control
+
+ # Draw `self` to `display`
+ fun draw(display: Display) do end
+
+ # Intercept and act upon events concerving `self`
+ fun input(event: InputEvent): Bool do return false
+end