Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/src/android/android_native_app_glue.cpp +437-0 View file
@@ -0,0 +1,437 @@
1+/*
2+ * Copyright (C) 2010 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ *
16+ */
17+
18+#include <jni.h>
19+
20+#include <errno.h>
21+#include <stdlib.h>
22+#include <string.h>
23+#include <unistd.h>
24+#include <sys/resource.h>
25+
26+#include "android_native_app_glue.h"
27+#include <android/log.h>
28+
29+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
30+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
31+
32+/* For debug builds, always enable the debug traces in this library */
33+#ifndef NDEBUG
34+# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
35+#else
36+# define LOGV(...) ((void)0)
37+#endif
38+
39+static void free_saved_state(struct android_app* android_app) {
40+ pthread_mutex_lock(&android_app->mutex);
41+ if (android_app->savedState != NULL) {
42+ free(android_app->savedState);
43+ android_app->savedState = NULL;
44+ android_app->savedStateSize = 0;
45+ }
46+ pthread_mutex_unlock(&android_app->mutex);
47+}
48+
49+int8_t android_app_read_cmd(struct android_app* android_app) {
50+ int8_t cmd;
51+ if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
52+ switch (cmd) {
53+ case APP_CMD_SAVE_STATE:
54+ free_saved_state(android_app);
55+ break;
56+ }
57+ return cmd;
58+ } else {
59+ LOGE("No data on command pipe!");
60+ }
61+ return -1;
62+}
63+
64+static void print_cur_config(struct android_app* android_app) {
65+ char lang[2], country[2];
66+ AConfiguration_getLanguage(android_app->config, lang);
67+ AConfiguration_getCountry(android_app->config, country);
68+
69+ LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
70+ "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
71+ "modetype=%d modenight=%d",
72+ AConfiguration_getMcc(android_app->config),
73+ AConfiguration_getMnc(android_app->config),
74+ lang[0], lang[1], country[0], country[1],
75+ AConfiguration_getOrientation(android_app->config),
76+ AConfiguration_getTouchscreen(android_app->config),
77+ AConfiguration_getDensity(android_app->config),
78+ AConfiguration_getKeyboard(android_app->config),
79+ AConfiguration_getNavigation(android_app->config),
80+ AConfiguration_getKeysHidden(android_app->config),
81+ AConfiguration_getNavHidden(android_app->config),
82+ AConfiguration_getSdkVersion(android_app->config),
83+ AConfiguration_getScreenSize(android_app->config),
84+ AConfiguration_getScreenLong(android_app->config),
85+ AConfiguration_getUiModeType(android_app->config),
86+ AConfiguration_getUiModeNight(android_app->config));
87+}
88+
89+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
90+ switch (cmd) {
91+ case APP_CMD_INPUT_CHANGED:
92+ LOGV("APP_CMD_INPUT_CHANGED\n");
93+ pthread_mutex_lock(&android_app->mutex);
94+ if (android_app->inputQueue != NULL) {
95+ AInputQueue_detachLooper(android_app->inputQueue);
96+ }
97+ android_app->inputQueue = android_app->pendingInputQueue;
98+ if (android_app->inputQueue != NULL) {
99+ LOGV("Attaching input queue to looper");
100+ AInputQueue_attachLooper(android_app->inputQueue,
101+ android_app->looper, LOOPER_ID_INPUT, NULL,
102+ &android_app->inputPollSource);
103+ }
104+ pthread_cond_broadcast(&android_app->cond);
105+ pthread_mutex_unlock(&android_app->mutex);
106+ break;
107+
108+ case APP_CMD_INIT_WINDOW:
109+ LOGV("APP_CMD_INIT_WINDOW\n");
110+ pthread_mutex_lock(&android_app->mutex);
111+ android_app->window = android_app->pendingWindow;
112+ pthread_cond_broadcast(&android_app->cond);
113+ pthread_mutex_unlock(&android_app->mutex);
114+ break;
115+
116+ case APP_CMD_TERM_WINDOW:
117+ LOGV("APP_CMD_TERM_WINDOW\n");
118+ pthread_cond_broadcast(&android_app->cond);
119+ break;
120+
121+ case APP_CMD_RESUME:
122+ case APP_CMD_START:
123+ case APP_CMD_PAUSE:
124+ case APP_CMD_STOP:
125+ LOGV("activityState=%d\n", cmd);
126+ pthread_mutex_lock(&android_app->mutex);
127+ android_app->activityState = cmd;
128+ pthread_cond_broadcast(&android_app->cond);
129+ pthread_mutex_unlock(&android_app->mutex);
130+ break;
131+
132+ case APP_CMD_CONFIG_CHANGED:
133+ LOGV("APP_CMD_CONFIG_CHANGED\n");
134+ AConfiguration_fromAssetManager(android_app->config,
135+ android_app->activity->assetManager);
136+ print_cur_config(android_app);
137+ break;
138+
139+ case APP_CMD_DESTROY:
140+ LOGV("APP_CMD_DESTROY\n");
141+ android_app->destroyRequested = 1;
142+ break;
143+ }
144+}
145+
146+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
147+ switch (cmd) {
148+ case APP_CMD_TERM_WINDOW:
149+ LOGV("APP_CMD_TERM_WINDOW\n");
150+ pthread_mutex_lock(&android_app->mutex);
151+ android_app->window = NULL;
152+ pthread_cond_broadcast(&android_app->cond);
153+ pthread_mutex_unlock(&android_app->mutex);
154+ break;
155+
156+ case APP_CMD_SAVE_STATE:
157+ LOGV("APP_CMD_SAVE_STATE\n");
158+ pthread_mutex_lock(&android_app->mutex);
159+ android_app->stateSaved = 1;
160+ pthread_cond_broadcast(&android_app->cond);
161+ pthread_mutex_unlock(&android_app->mutex);
162+ break;
163+
164+ case APP_CMD_RESUME:
165+ free_saved_state(android_app);
166+ break;
167+ }
168+}
169+
170+static void android_app_destroy(struct android_app* android_app) {
171+ LOGV("android_app_destroy!");
172+ free_saved_state(android_app);
173+ pthread_mutex_lock(&android_app->mutex);
174+ if (android_app->inputQueue != NULL) {
175+ AInputQueue_detachLooper(android_app->inputQueue);
176+ }
177+ AConfiguration_delete(android_app->config);
178+ android_app->destroyed = 1;
179+ pthread_cond_broadcast(&android_app->cond);
180+ pthread_mutex_unlock(&android_app->mutex);
181+ // Can't touch android_app object after this.
182+}
183+
184+static void process_input(struct android_app* app, struct android_poll_source* source) {
185+ AInputEvent* event = NULL;
186+ while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
187+ LOGV("New input event: type=%d\n", AInputEvent_getType(event));
188+ if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
189+ continue;
190+ }
191+ int32_t handled = 0;
192+ if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
193+ AInputQueue_finishEvent(app->inputQueue, event, handled);
194+ }
195+}
196+
197+static void process_cmd(struct android_app* app, struct android_poll_source* source) {
198+ int8_t cmd = android_app_read_cmd(app);
199+ android_app_pre_exec_cmd(app, cmd);
200+ if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
201+ android_app_post_exec_cmd(app, cmd);
202+}
203+
204+static void* android_app_entry(void* param) {
205+ struct android_app* android_app = (struct android_app*)param;
206+
207+ android_app->config = AConfiguration_new();
208+ AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
209+
210+ print_cur_config(android_app);
211+
212+ android_app->cmdPollSource.id = LOOPER_ID_MAIN;
213+ android_app->cmdPollSource.app = android_app;
214+ android_app->cmdPollSource.process = process_cmd;
215+ android_app->inputPollSource.id = LOOPER_ID_INPUT;
216+ android_app->inputPollSource.app = android_app;
217+ android_app->inputPollSource.process = process_input;
218+
219+ ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
220+ ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
221+ &android_app->cmdPollSource);
222+ android_app->looper = looper;
223+
224+ pthread_mutex_lock(&android_app->mutex);
225+ android_app->running = 1;
226+ pthread_cond_broadcast(&android_app->cond);
227+ pthread_mutex_unlock(&android_app->mutex);
228+
229+ android_main(android_app);
230+
231+ android_app_destroy(android_app);
232+ return NULL;
233+}
234+
235+// --------------------------------------------------------------------
236+// Native activity interaction (called from main thread)
237+// --------------------------------------------------------------------
238+
239+static struct android_app* android_app_create(ANativeActivity* activity,
240+ void* savedState, size_t savedStateSize) {
241+ struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
242+ memset(android_app, 0, sizeof(struct android_app));
243+ android_app->activity = activity;
244+
245+ pthread_mutex_init(&android_app->mutex, NULL);
246+ pthread_cond_init(&android_app->cond, NULL);
247+
248+ if (savedState != NULL) {
249+ android_app->savedState = malloc(savedStateSize);
250+ android_app->savedStateSize = savedStateSize;
251+ memcpy(android_app->savedState, savedState, savedStateSize);
252+ }
253+
254+ int msgpipe[2];
255+ if (pipe(msgpipe)) {
256+ LOGE("could not create pipe: %s", strerror(errno));
257+ return NULL;
258+ }
259+ android_app->msgread = msgpipe[0];
260+ android_app->msgwrite = msgpipe[1];
261+
262+ pthread_attr_t attr;
263+ pthread_attr_init(&attr);
264+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
265+ pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
266+
267+ // Wait for thread to start.
268+ pthread_mutex_lock(&android_app->mutex);
269+ while (!android_app->running) {
270+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
271+ }
272+ pthread_mutex_unlock(&android_app->mutex);
273+
274+ return android_app;
275+}
276+
277+static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
278+ if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
279+ LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
280+ }
281+}
282+
283+static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
284+ pthread_mutex_lock(&android_app->mutex);
285+ android_app->pendingInputQueue = inputQueue;
286+ android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
287+ while (android_app->inputQueue != android_app->pendingInputQueue) {
288+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
289+ }
290+ pthread_mutex_unlock(&android_app->mutex);
291+}
292+
293+static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
294+ pthread_mutex_lock(&android_app->mutex);
295+ if (android_app->pendingWindow != NULL) {
296+ android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
297+ }
298+ android_app->pendingWindow = window;
299+ if (window != NULL) {
300+ android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
301+ }
302+ while (android_app->window != android_app->pendingWindow) {
303+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
304+ }
305+ pthread_mutex_unlock(&android_app->mutex);
306+}
307+
308+static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
309+ pthread_mutex_lock(&android_app->mutex);
310+ android_app_write_cmd(android_app, cmd);
311+ while (android_app->activityState != cmd) {
312+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
313+ }
314+ pthread_mutex_unlock(&android_app->mutex);
315+}
316+
317+static void android_app_free(struct android_app* android_app) {
318+ pthread_mutex_lock(&android_app->mutex);
319+ android_app_write_cmd(android_app, APP_CMD_DESTROY);
320+ while (!android_app->destroyed) {
321+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
322+ }
323+ pthread_mutex_unlock(&android_app->mutex);
324+
325+ close(android_app->msgread);
326+ close(android_app->msgwrite);
327+ pthread_cond_destroy(&android_app->cond);
328+ pthread_mutex_destroy(&android_app->mutex);
329+ free(android_app);
330+}
331+
332+static void onDestroy(ANativeActivity* activity) {
333+ LOGV("Destroy: %p\n", activity);
334+ android_app_free((struct android_app*)activity->instance);
335+}
336+
337+static void onStart(ANativeActivity* activity) {
338+ LOGV("Start: %p\n", activity);
339+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
340+}
341+
342+static void onResume(ANativeActivity* activity) {
343+ LOGV("Resume: %p\n", activity);
344+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
345+}
346+
347+static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
348+ struct android_app* android_app = (struct android_app*)activity->instance;
349+ void* savedState = NULL;
350+
351+ LOGV("SaveInstanceState: %p\n", activity);
352+ pthread_mutex_lock(&android_app->mutex);
353+ android_app->stateSaved = 0;
354+ android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
355+ while (!android_app->stateSaved) {
356+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
357+ }
358+
359+ if (android_app->savedState != NULL) {
360+ savedState = android_app->savedState;
361+ *outLen = android_app->savedStateSize;
362+ android_app->savedState = NULL;
363+ android_app->savedStateSize = 0;
364+ }
365+
366+ pthread_mutex_unlock(&android_app->mutex);
367+
368+ return savedState;
369+}
370+
371+static void onPause(ANativeActivity* activity) {
372+ LOGV("Pause: %p\n", activity);
373+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
374+}
375+
376+static void onStop(ANativeActivity* activity) {
377+ LOGV("Stop: %p\n", activity);
378+ android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
379+}
380+
381+static void onConfigurationChanged(ANativeActivity* activity) {
382+ struct android_app* android_app = (struct android_app*)activity->instance;
383+ LOGV("ConfigurationChanged: %p\n", activity);
384+ android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
385+}
386+
387+static void onLowMemory(ANativeActivity* activity) {
388+ struct android_app* android_app = (struct android_app*)activity->instance;
389+ LOGV("LowMemory: %p\n", activity);
390+ android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
391+}
392+
393+static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
394+ LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
395+ android_app_write_cmd((struct android_app*)activity->instance,
396+ focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
397+}
398+
399+static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
400+ LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
401+ android_app_set_window((struct android_app*)activity->instance, window);
402+}
403+
404+static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
405+ LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
406+ android_app_set_window((struct android_app*)activity->instance, NULL);
407+}
408+
409+static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
410+ LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
411+ android_app_set_input((struct android_app*)activity->instance, queue);
412+}
413+
414+static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
415+ LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
416+ android_app_set_input((struct android_app*)activity->instance, NULL);
417+}
418+
419+void ANativeActivity_onCreate(ANativeActivity* activity,
420+ void* savedState, size_t savedStateSize) {
421+ LOGV("Creating: %p\n", activity);
422+ activity->callbacks->onDestroy = onDestroy;
423+ activity->callbacks->onStart = onStart;
424+ activity->callbacks->onResume = onResume;
425+ activity->callbacks->onSaveInstanceState = onSaveInstanceState;
426+ activity->callbacks->onPause = onPause;
427+ activity->callbacks->onStop = onStop;
428+ activity->callbacks->onConfigurationChanged = onConfigurationChanged;
429+ activity->callbacks->onLowMemory = onLowMemory;
430+ activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
431+ activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
432+ activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
433+ activity->callbacks->onInputQueueCreated = onInputQueueCreated;
434+ activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
435+
436+ activity->instance = android_app_create(activity, savedState, savedStateSize);
437+}
A RD_Client/src/android/android_native_app_glue.h +344-0 View file
@@ -0,0 +1,344 @@
1+/*
2+ * Copyright (C) 2010 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ *
16+ */
17+
18+#ifndef _ANDROID_NATIVE_APP_GLUE_H
19+#define _ANDROID_NATIVE_APP_GLUE_H
20+
21+#include <poll.h>
22+#include <pthread.h>
23+#include <sched.h>
24+
25+#include <android/configuration.h>
26+#include <android/looper.h>
27+#include <android/native_activity.h>
28+
29+//#ifdef __cplusplus
30+//extern "C" {
31+//#endif
32+
33+/**
34+ * The native activity interface provided by <android/native_activity.h>
35+ * is based on a set of application-provided callbacks that will be called
36+ * by the Activity's main thread when certain events occur.
37+ *
38+ * This means that each one of this callbacks _should_ _not_ block, or they
39+ * risk having the system force-close the application. This programming
40+ * model is direct, lightweight, but constraining.
41+ *
42+ * The 'threaded_native_app' static library is used to provide a different
43+ * execution model where the application can implement its own main event
44+ * loop in a different thread instead. Here's how it works:
45+ *
46+ * 1/ The application must provide a function named "android_main()" that
47+ * will be called when the activity is created, in a new thread that is
48+ * distinct from the activity's main thread.
49+ *
50+ * 2/ android_main() receives a pointer to a valid "android_app" structure
51+ * that contains references to other important objects, e.g. the
52+ * ANativeActivity obejct instance the application is running in.
53+ *
54+ * 3/ the "android_app" object holds an ALooper instance that already
55+ * listens to two important things:
56+ *
57+ * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
58+ * declarations below.
59+ *
60+ * - input events coming from the AInputQueue attached to the activity.
61+ *
62+ * Each of these correspond to an ALooper identifier returned by
63+ * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
64+ * respectively.
65+ *
66+ * Your application can use the same ALooper to listen to additional
67+ * file-descriptors. They can either be callback based, or with return
68+ * identifiers starting with LOOPER_ID_USER.
69+ *
70+ * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
71+ * the returned data will point to an android_poll_source structure. You
72+ * can call the process() function on it, and fill in android_app->onAppCmd
73+ * and android_app->onInputEvent to be called for your own processing
74+ * of the event.
75+ *
76+ * Alternatively, you can call the low-level functions to read and process
77+ * the data directly... look at the process_cmd() and process_input()
78+ * implementations in the glue to see how to do this.
79+ *
80+ * See the sample named "native-activity" that comes with the NDK with a
81+ * full usage example. Also look at the JavaDoc of NativeActivity.
82+ */
83+
84+struct android_app;
85+
86+/**
87+ * Data associated with an ALooper fd that will be returned as the "outData"
88+ * when that source has data ready.
89+ */
90+struct android_poll_source {
91+ // The identifier of this source. May be LOOPER_ID_MAIN or
92+ // LOOPER_ID_INPUT.
93+ int32_t id;
94+
95+ // The android_app this ident is associated with.
96+ struct android_app* app;
97+
98+ // Function to call to perform the standard processing of data from
99+ // this source.
100+ void (*process)(struct android_app* app, struct android_poll_source* source);
101+};
102+
103+/**
104+ * This is the interface for the standard glue code of a threaded
105+ * application. In this model, the application's code is running
106+ * in its own thread separate from the main thread of the process.
107+ * It is not required that this thread be associated with the Java
108+ * VM, although it will need to be in order to make JNI calls any
109+ * Java objects.
110+ */
111+struct android_app {
112+ // The application can place a pointer to its own state object
113+ // here if it likes.
114+ void* userData;
115+
116+ // Fill this in with the function to process main app commands (APP_CMD_*)
117+ void (*onAppCmd)(struct android_app* app, int32_t cmd);
118+
119+ // Fill this in with the function to process input events. At this point
120+ // the event has already been pre-dispatched, and it will be finished upon
121+ // return. Return 1 if you have handled the event, 0 for any default
122+ // dispatching.
123+ int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
124+
125+ // The ANativeActivity object instance that this app is running in.
126+ ANativeActivity* activity;
127+
128+ // The current configuration the app is running in.
129+ AConfiguration* config;
130+
131+ // This is the last instance's saved state, as provided at creation time.
132+ // It is NULL if there was no state. You can use this as you need; the
133+ // memory will remain around until you call android_app_exec_cmd() for
134+ // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL.
135+ // These variables should only be changed when processing a APP_CMD_SAVE_STATE,
136+ // at which point they will be initialized to NULL and you can malloc your
137+ // state and place the information here. In that case the memory will be
138+ // freed for you later.
139+ void* savedState;
140+ size_t savedStateSize;
141+
142+ // The ALooper associated with the app's thread.
143+ ALooper* looper;
144+
145+ // When non-NULL, this is the input queue from which the app will
146+ // receive user input events.
147+ AInputQueue* inputQueue;
148+
149+ // When non-NULL, this is the window surface that the app can draw in.
150+ ANativeWindow* window;
151+
152+ // Current content rectangle of the window; this is the area where the
153+ // window's content should be placed to be seen by the user.
154+ ARect contentRect;
155+
156+ // Current state of the app's activity. May be either APP_CMD_START,
157+ // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
158+ int activityState;
159+
160+ // This is non-zero when the application's NativeActivity is being
161+ // destroyed and waiting for the app thread to complete.
162+ int destroyRequested;
163+
164+ // -------------------------------------------------
165+ // Below are "private" implementation of the glue code.
166+
167+ pthread_mutex_t mutex;
168+ pthread_cond_t cond;
169+
170+ int msgread;
171+ int msgwrite;
172+
173+ pthread_t thread;
174+
175+ struct android_poll_source cmdPollSource;
176+ struct android_poll_source inputPollSource;
177+
178+ int running;
179+ int stateSaved;
180+ int destroyed;
181+ int redrawNeeded;
182+ AInputQueue* pendingInputQueue;
183+ ANativeWindow* pendingWindow;
184+ ARect pendingContentRect;
185+};
186+
187+enum {
188+ /**
189+ * Looper data ID of commands coming from the app's main thread, which
190+ * is returned as an identifier from ALooper_pollOnce(). The data for this
191+ * identifier is a pointer to an android_poll_source structure.
192+ * These can be retrieved and processed with android_app_read_cmd()
193+ * and android_app_exec_cmd().
194+ */
195+ LOOPER_ID_MAIN = 1,
196+
197+ /**
198+ * Looper data ID of events coming from the AInputQueue of the
199+ * application's window, which is returned as an identifier from
200+ * ALooper_pollOnce(). The data for this identifier is a pointer to an
201+ * android_poll_source structure. These can be read via the inputQueue
202+ * object of android_app.
203+ */
204+ LOOPER_ID_INPUT = 2,
205+
206+ /**
207+ * Start of user-defined ALooper identifiers.
208+ */
209+ LOOPER_ID_USER = 3,
210+};
211+
212+enum {
213+ /**
214+ * Command from main thread: the AInputQueue has changed. Upon processing
215+ * this command, android_app->inputQueue will be updated to the new queue
216+ * (or NULL).
217+ */
218+ APP_CMD_INPUT_CHANGED,
219+
220+ /**
221+ * Command from main thread: a new ANativeWindow is ready for use. Upon
222+ * receiving this command, android_app->window will contain the new window
223+ * surface.
224+ */
225+ APP_CMD_INIT_WINDOW,
226+
227+ /**
228+ * Command from main thread: the existing ANativeWindow needs to be
229+ * terminated. Upon receiving this command, android_app->window still
230+ * contains the existing window; after calling android_app_exec_cmd
231+ * it will be set to NULL.
232+ */
233+ APP_CMD_TERM_WINDOW,
234+
235+ /**
236+ * Command from main thread: the current ANativeWindow has been resized.
237+ * Please redraw with its new size.
238+ */
239+ APP_CMD_WINDOW_RESIZED,
240+
241+ /**
242+ * Command from main thread: the system needs that the current ANativeWindow
243+ * be redrawn. You should redraw the window before handing this to
244+ * android_app_exec_cmd() in order to avoid transient drawing glitches.
245+ */
246+ APP_CMD_WINDOW_REDRAW_NEEDED,
247+
248+ /**
249+ * Command from main thread: the content area of the window has changed,
250+ * such as from the soft input window being shown or hidden. You can
251+ * find the new content rect in android_app::contentRect.
252+ */
253+ APP_CMD_CONTENT_RECT_CHANGED,
254+
255+ /**
256+ * Command from main thread: the app's activity window has gained
257+ * input focus.
258+ */
259+ APP_CMD_GAINED_FOCUS,
260+
261+ /**
262+ * Command from main thread: the app's activity window has lost
263+ * input focus.
264+ */
265+ APP_CMD_LOST_FOCUS,
266+
267+ /**
268+ * Command from main thread: the current device configuration has changed.
269+ */
270+ APP_CMD_CONFIG_CHANGED,
271+
272+ /**
273+ * Command from main thread: the system is running low on memory.
274+ * Try to reduce your memory use.
275+ */
276+ APP_CMD_LOW_MEMORY,
277+
278+ /**
279+ * Command from main thread: the app's activity has been started.
280+ */
281+ APP_CMD_START,
282+
283+ /**
284+ * Command from main thread: the app's activity has been resumed.
285+ */
286+ APP_CMD_RESUME,
287+
288+ /**
289+ * Command from main thread: the app should generate a new saved state
290+ * for itself, to restore from later if needed. If you have saved state,
291+ * allocate it with malloc and place it in android_app.savedState with
292+ * the size in android_app.savedStateSize. The will be freed for you
293+ * later.
294+ */
295+ APP_CMD_SAVE_STATE,
296+
297+ /**
298+ * Command from main thread: the app's activity has been paused.
299+ */
300+ APP_CMD_PAUSE,
301+
302+ /**
303+ * Command from main thread: the app's activity has been stopped.
304+ */
305+ APP_CMD_STOP,
306+
307+ /**
308+ * Command from main thread: the app's activity is being destroyed,
309+ * and waiting for the app thread to clean up and exit before proceeding.
310+ */
311+ APP_CMD_DESTROY,
312+};
313+
314+/**
315+ * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
316+ * app command message.
317+ */
318+int8_t android_app_read_cmd(struct android_app* android_app);
319+
320+/**
321+ * Call with the command returned by android_app_read_cmd() to do the
322+ * initial pre-processing of the given command. You can perform your own
323+ * actions for the command after calling this function.
324+ */
325+void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
326+
327+/**
328+ * Call with the command returned by android_app_read_cmd() to do the
329+ * final post-processing of the given command. You must have done your own
330+ * actions for the command before calling this function.
331+ */
332+void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
333+
334+/**
335+ * This is the function that application code must implement, representing
336+ * the main entry to the app.
337+ */
338+extern void android_main(struct android_app* app);
339+
340+//#ifdef __cplusplus
341+//}
342+//#endif
343+
344+#endif /* _ANDROID_NATIVE_APP_GLUE_H */
A RD_Client/src/android/dependencies.cpp +29-0 View file
@@ -0,0 +1,29 @@
1+#if (__ANDROID_API__ > 19)
2+#include <android/api-level.h>
3+#include <android/log.h>
4+#include <signal.h>
5+#include <dlfcn.h>
6+
7+extern "C" {
8+ typedef __sighandler_t(*bsd_signal_func_t)(int, __sighandler_t);
9+ bsd_signal_func_t bsd_signal_func = NULL;
10+
11+ __sighandler_t bsd_signal(int s, __sighandler_t f)
12+ {
13+ if (bsd_signal_func == NULL) {
14+ // For now (up to Android 7.0) this is always available
15+ bsd_signal_func = (bsd_signal_func_t)dlsym(RTLD_DEFAULT, "bsd_signal");
16+
17+ if (bsd_signal_func == NULL) {
18+ // You may try dlsym(RTLD_DEFAULT, "signal") or dlsym(RTLD_NEXT, "signal") here
19+ // Make sure you add a comment here in StackOverflow
20+ // if you find a device that doesn't have "bsd_signal" in its libc.so!!!
21+
22+ __android_log_assert("", "bsd_signal_wrapper", "bsd_signal symbol not found!");
23+ }
24+ }
25+
26+ return bsd_signal_func(s, f);
27+ }
28+}
29+#endif
A RD_Client/src/android/pch.h +28-0 View file
@@ -0,0 +1,28 @@
1+//
2+// pch.h
3+// Header for standard system include files.
4+//
5+// Used by the build system to generate the precompiled header. Note that no
6+// pch.cpp is needed and the pch.h is automatically included in all cpp files
7+// that are part of the project
8+//
9+
10+#include <jni.h>
11+#include <errno.h>
12+
13+#include <string.h>
14+#include <unistd.h>
15+#include <sys/resource.h>
16+
17+#include <EGL/egl.h>
18+#include <GLES/gl.h>
19+
20+#include <android/sensor.h>
21+
22+#include <android/log.h>
23+#include <android/native_window.h>
24+#include <android/native_window_jni.h>
25+
26+#include "android_native_app_glue.h"
27+
28+extern android_app* g_androidState;
A RD_Client/src/client/1lightview.cpp +94-0 View file
@@ -0,0 +1,94 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "lightview.h"
24+#include "spritemanager.h"
25+#include <framework/graphics/painter.h>
26+
27+void LightView::addLight(const Point& pos, uint8_t color, uint8_t intensity)
28+{
29+ if (!m_lights.empty()) {
30+ Light& prevLight = m_lights.back();
31+ if (prevLight.pos == pos && prevLight.color == color) {
32+ prevLight.intensity = std::max(prevLight.intensity, intensity);
33+ return;
34+ }
35+ }
36+ m_lights.push_back(Light{ pos, color, intensity });
37+}
38+
39+void LightView::setFieldBrightness(const Point& pos, size_t start, uint8_t color)
40+{
41+ size_t index = (pos.y / g_sprites.spriteSize()) * m_mapSize.width() + (pos.x / g_sprites.spriteSize());
42+ if (index >= m_tiles.size()) return;
43+ m_tiles[index].start = start;
44+ m_tiles[index].color = color;
45+}
46+
47+void LightView::draw() // render thread
48+{
49+ // TODO: optimize in the future for big areas
50+ static std::vector<uint8_t> buffer;
51+ if (buffer.size() < 4u * m_mapSize.area())
52+ buffer.resize(m_mapSize.area() * 4);
53+
54+ for (int x = 0; x < m_mapSize.width(); ++x) {
55+ for (int y = 0; y < m_mapSize.height(); ++y) {
56+ Point pos(x * g_sprites.spriteSize() + g_sprites.spriteSize() / 2, y * g_sprites.spriteSize() + g_sprites.spriteSize() / 2);
57+ int index = (y * m_mapSize.width() + x);
58+ int colorIndex = index * 4;
59+ buffer[colorIndex] = m_globalLight.r();
60+ buffer[colorIndex + 1] = m_globalLight.g();
61+ buffer[colorIndex + 2] = m_globalLight.b();
62+ buffer[colorIndex + 3] = 255; // alpha channel
63+ for (size_t i = m_tiles[index].start; i < m_lights.size(); ++i) {
64+ Light& light = m_lights[i];
65+ float distance = std::sqrt((pos.x - light.pos.x) * (pos.x - light.pos.x) +
66+ (pos.y - light.pos.y) * (pos.y - light.pos.y));
67+ distance /= g_sprites.spriteSize();
68+ float intensity = (-distance + light.intensity) * 0.2f;
69+ if (intensity < 0.01f) continue;
70+ if (intensity > 1.0f) intensity = 1.0f;
71+ Color lightColor = Color::from8bit(light.color) * intensity;
72+ buffer[colorIndex] = std::max<int>(buffer[colorIndex], lightColor.r());
73+ buffer[colorIndex + 1] = std::max<int>(buffer[colorIndex + 1], lightColor.g());
74+ buffer[colorIndex + 2] = std::max<int>(buffer[colorIndex + 2], lightColor.b());
75+ }
76+ }
77+ }
78+
79+ m_lightTexture->update();
80+ glBindTexture(GL_TEXTURE_2D, m_lightTexture->getId());
81+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_mapSize.width(), m_mapSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
82+
83+ Point offset = m_src.topLeft();
84+ Size size = m_src.size();
85+ CoordsBuffer coords;
86+ coords.addRect(RectF(m_dest.left(), m_dest.top(), m_dest.width(), m_dest.height()),
87+ RectF((float)offset.x / g_sprites.spriteSize(), (float)offset.y / g_sprites.spriteSize(),
88+ (float)size.width() / g_sprites.spriteSize(), (float)size.height() / g_sprites.spriteSize()));
89+
90+ g_painter->resetColor();
91+ g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
92+ g_painter->drawTextureCoords(coords, m_lightTexture);
93+ g_painter->resetCompositionMode();
94+}
A RD_Client/src/client/1minimap.cpp +531-0 View file
@@ -0,0 +1,531 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "minimap.h"
24+#include "game.h"
25+#include "spritemanager.h"
26+#include "tile.h"
27+
28+#include <framework/core/filestream.h>
29+#include <framework/core/resourcemanager.h>
30+#include <framework/graphics/framebuffermanager.h>
31+#include <framework/graphics/image.h>
32+#include <framework/graphics/painter.h>
33+#include <framework/graphics/texture.h>
34+#include <zlib.h>
35+
36+#include <framework/util/stats.h>
37+
38+Minimap g_minimap;
39+
40+void MinimapBlock::clean() {
41+ m_tiles.fill(MinimapTile());
42+ m_texture.reset();
43+ m_mustUpdate = false;
44+}
45+
46+void MinimapBlock::update() {
47+ if (!m_mustUpdate)
48+ return;
49+
50+ ImagePtr image(new Image(Size(MMBLOCK_SIZE, MMBLOCK_SIZE)));
51+
52+ bool shouldDraw = false;
53+ for (int x = 0; x < MMBLOCK_SIZE; ++x) {
54+ for (int y = 0; y < MMBLOCK_SIZE; ++y) {
55+ uint8 c = getTile(x, y).color;
56+ Color col = Color::alpha;
57+ if (c != 255) {
58+ col = Color::from8bit(c);
59+ shouldDraw = true;
60+ }
61+ image->setPixel(x, y, col);
62+ }
63+ }
64+
65+ if (shouldDraw) {
66+ m_texture = TexturePtr(new Texture(image));
67+ } else
68+ m_texture.reset();
69+
70+ m_mustUpdate = false;
71+}
72+
73+void MinimapBlock::updateTile(int x, int y, const MinimapTile &tile) {
74+ if (m_tiles[getTileIndex(x, y)].color != tile.color)
75+ m_mustUpdate = true;
76+
77+ m_tiles[getTileIndex(x, y)] = tile;
78+}
79+
80+void Minimap::init() {}
81+
82+void Minimap::terminate() { clean(); }
83+
84+void Minimap::clean() {
85+ std::lock_guard<std::mutex> lock(m_lock);
86+ for (int i = 0; i <= Otc::MAX_Z; ++i)
87+ m_tileBlocks[i].clear();
88+}
89+
90+void Minimap::draw(const Rect &screenRect, const Position &mapCenter,
91+ float scale, const Color &color) {
92+ if (screenRect.isEmpty())
93+ return;
94+
95+ Rect mapRect =
96+ calcMapRect(screenRect, mapCenter,
97+ scale); // Draw background (disabled to avoid square shape)
98+ // g_drawQueue->addFilledRect(screenRect, color);
99+
100+ if (MMBLOCK_SIZE * scale <= 1 || !mapCenter.isMapPosition()) {
101+ return;
102+ }
103+
104+ size_t drawQueueStart = g_drawQueue->size();
105+ Point blockOff = getBlockOffset(mapRect.topLeft());
106+ Point off =
107+ Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint()) /
108+ 2;
109+ Point start =
110+ screenRect.topLeft() - (mapRect.topLeft() - blockOff) * scale - off;
111+
112+ // Circle clip: compute center and squared radius from screenRect
113+ int circleX = screenRect.left() + screenRect.width() / 2;
114+ int circleY = screenRect.top() + screenRect.height() / 2;
115+ int circleR = std::min(screenRect.width(), screenRect.height()) / 2;
116+ int circleR2 = circleR * circleR;
117+
118+ for (int y = blockOff.y, ys = start.y; ys < screenRect.bottom();
119+ y += MMBLOCK_SIZE, ys += MMBLOCK_SIZE * scale) {
120+ if (y < 0 || y >= 65536)
121+ continue;
122+
123+ for (int x = blockOff.x, xs = start.x; xs < screenRect.right();
124+ x += MMBLOCK_SIZE, xs += MMBLOCK_SIZE * scale) {
125+ if (x < 0 || x >= 65536)
126+ continue;
127+
128+ Position blockPos(x, y, mapCenter.z);
129+ if (!hasBlock(blockPos))
130+ continue;
131+
132+ MinimapBlock &block = getBlock(Position(x, y, mapCenter.z));
133+ block.update();
134+
135+ const TexturePtr &tex = block.getTexture();
136+ if (tex) {
137+ Rect src(0, 0, MMBLOCK_SIZE, MMBLOCK_SIZE);
138+ float fscale = (float)scale;
139+ Rect dest(xs, ys, (int)(MMBLOCK_SIZE * fscale),
140+ (int)(MMBLOCK_SIZE * fscale));
141+
142+ // Circle check: skip tiles whose nearest point to circle center
143+ // is outside the circle radius (corners won't render outside ring).
144+ {
145+ int nearX = std::max(dest.left(), std::min(circleX, dest.right()));
146+ int nearY = std::max(dest.top(), std::min(circleY, dest.bottom()));
147+ int dx = nearX - circleX;
148+ int dy = nearY - circleY;
149+ if (dx * dx + dy * dy > circleR2)
150+ continue;
151+ }
152+
153+ // Sub-block drawing for smoother circular edges
154+ int subDiv = 64;
155+ int subSize = MMBLOCK_SIZE / subDiv;
156+ float subDestSizeX = (float)dest.width() / (float)subDiv;
157+ float subDestSizeY = (float)dest.height() / (float)subDiv;
158+ float subSrcSizeX = (float)src.width() / (float)subDiv;
159+ float subSrcSizeY = (float)src.height() / (float)subDiv;
160+
161+ for (int sy = 0; sy < subDiv; ++sy) {
162+ int yStart = (int)(sy * subDestSizeY);
163+ int yEnd = (int)((sy + 1) * subDestSizeY);
164+ int syStart = (int)(sy * subSrcSizeY);
165+ int syEnd = (int)((sy + 1) * subSrcSizeY);
166+
167+ for (int sx = 0; sx < subDiv; ++sx) {
168+ int xStart = (int)(sx * subDestSizeX);
169+ int xEnd = (int)((sx + 1) * subDestSizeX);
170+
171+ Rect subDest(dest.left() + xStart, dest.top() + yStart,
172+ xEnd - xStart, yEnd - yStart);
173+
174+ // Check if all four corners of subDest are within the circle
175+ bool inside = true;
176+ Point corners[4] = {subDest.topLeft(), subDest.topRight(),
177+ subDest.bottomLeft(), subDest.bottomRight()};
178+ for (const Point &p : corners) {
179+ int dx = p.x - circleX;
180+ int dy = p.y - circleY;
181+ if (dx * dx + dy * dy > circleR2) {
182+ inside = false;
183+ break;
184+ }
185+ }
186+ if (!inside)
187+ continue;
188+
189+ int sxStart = (int)(sx * subSrcSizeX);
190+ int sxEnd = (int)((sx + 1) * subSrcSizeX);
191+
192+ Rect subSrc(src.left() + sxStart, src.top() + syStart,
193+ sxEnd - sxStart, syEnd - syStart);
194+
195+ // Rect clip: clip subDest to screenRect
196+ if (!screenRect.contains(subDest)) {
197+ if (!subDest.intersects(screenRect))
198+ continue;
199+ }
200+
201+ g_drawQueue->addTexturedRect(subDest, tex, subSrc);
202+ }
203+ }
204+ }
205+ }
206+ }
207+
208+ g_drawQueue->setClip(drawQueueStart, screenRect);
209+}
210+
211+Point Minimap::getTilePoint(const Position &pos, const Rect &screenRect,
212+ const Position &mapCenter, float scale) {
213+ if (screenRect.isEmpty() || pos.z != mapCenter.z)
214+ return Point(-1, -1);
215+
216+ Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
217+ Point off =
218+ Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint()) /
219+ 2;
220+ Point posoff = (Point(pos.x, pos.y) - mapRect.topLeft()) * scale;
221+ return posoff + screenRect.topLeft() - off + (Point(1, 1) * scale) / 2;
222+}
223+
224+Position Minimap::getTilePosition(const Point &point, const Rect &screenRect,
225+ const Position &mapCenter, float scale) {
226+ if (screenRect.isEmpty())
227+ return Position();
228+
229+ Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
230+ Point off =
231+ Point((mapRect.size() * scale).toPoint() - screenRect.size().toPoint()) /
232+ 2;
233+ Point pos2d =
234+ (point - screenRect.topLeft() + off) / scale + mapRect.topLeft();
235+ return Position(pos2d.x, pos2d.y, mapCenter.z);
236+}
237+
238+Rect Minimap::getTileRect(const Position &pos, const Rect &screenRect,
239+ const Position &mapCenter, float scale) {
240+ if (screenRect.isEmpty() || pos.z != mapCenter.z)
241+ return Rect();
242+
243+ int tileSize = g_sprites.spriteSize() * scale;
244+ Rect tileRect(0, 0, tileSize, tileSize);
245+ tileRect.moveCenter(getTilePoint(pos, screenRect, mapCenter, scale));
246+ return tileRect;
247+}
248+
249+Rect Minimap::calcMapRect(const Rect &screenRect, const Position &mapCenter,
250+ float scale) {
251+ int w = screenRect.width() / scale,
252+ h = std::ceil(screenRect.height() / scale);
253+ Rect mapRect(0, 0, w, h);
254+ mapRect.moveCenter(Point(mapCenter.x, mapCenter.y));
255+ return mapRect;
256+}
257+
258+void Minimap::updateTile(const Position &pos, const TilePtr &tile) {
259+ MinimapTile minimapTile;
260+ if (tile) {
261+ minimapTile.color = tile->getMinimapColorByte();
262+ minimapTile.flags |= MinimapTileWasSeen;
263+ if (!tile->isWalkable(true))
264+ minimapTile.flags |= MinimapTileNotWalkable;
265+ if (!tile->isPathable())
266+ minimapTile.flags |= MinimapTileNotPathable;
267+ minimapTile.speed =
268+ std::min<int>((int)std::ceil(tile->getGroundSpeed() / 10.0f), 255);
269+ } else {
270+ minimapTile.color = 255;
271+ minimapTile.flags |= MinimapTileEmpty;
272+ minimapTile.speed = 1;
273+ }
274+
275+ if (minimapTile != MinimapTile()) {
276+ MinimapBlock &block = getBlock(pos);
277+ Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
278+ block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile);
279+ block.justSaw();
280+ }
281+}
282+
283+const MinimapTile &Minimap::getTile(const Position &pos) {
284+ static MinimapTile nulltile;
285+ if (pos.z <= Otc::MAX_Z && hasBlock(pos)) {
286+ MinimapBlock &block = getBlock(pos);
287+ Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
288+ return block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
289+ }
290+ return nulltile;
291+}
292+
293+std::pair<MinimapBlock_ptr, MinimapTile>
294+Minimap::threadGetTile(const Position &pos) {
295+ std::lock_guard<std::mutex> lock(m_lock);
296+ static MinimapTile nulltile;
297+
298+ if (pos.z <= Otc::MAX_Z && hasBlock(pos)) {
299+ MinimapBlock_ptr block = m_tileBlocks[pos.z][getBlockIndex(pos)];
300+ if (block) {
301+ Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
302+ return std::make_pair(
303+ block, block->getTile(pos.x - offsetPos.x, pos.y - offsetPos.y));
304+ }
305+ }
306+ return std::make_pair(nullptr, nulltile);
307+}
308+
309+bool Minimap::loadImage(const std::string &fileName, const Position &topLeft,
310+ float colorFactor) {
311+ if (colorFactor <= 0.01f)
312+ colorFactor = 1.0f;
313+
314+ try {
315+ ImagePtr image = Image::load(fileName);
316+
317+ uint8 waterc = Color::to8bit(std::string("#3300cc"));
318+
319+ // non pathable colors
320+ Color nonPathableColors[] = {
321+ std::string("#ffff00"), // yellow
322+ };
323+
324+ // non walkable colors
325+ Color nonWalkableColors[] = {
326+ std::string("#000000"), // oil, black
327+ std::string("#006600"), // trees, dark green
328+ std::string("#ff3300"), // walls, red
329+ std::string("#666666"), // mountain, grey
330+ std::string("#ff6600"), // lava, orange
331+ std::string("#00ff00"), // positon
332+ std::string("#ccffff"), // ice, very light blue
333+ };
334+
335+ for (int y = 0; y < image->getHeight(); ++y) {
336+ for (int x = 0; x < image->getWidth(); ++x) {
337+ Color color = *(uint32 *)image->getPixel(x, y);
338+ uint8 c = Color::to8bit(color * colorFactor);
339+ int flags = 0;
340+
341+ if (c == waterc || color.a() == 0) {
342+ flags |= MinimapTileNotWalkable;
343+ c = 255; // alpha
344+ }
345+
346+ if (flags != 0) {
347+ for (Color &col : nonWalkableColors) {
348+ if (col == color) {
349+ flags |= MinimapTileNotWalkable;
350+ break;
351+ }
352+ }
353+ }
354+
355+ if (flags != 0) {
356+ for (Color &col : nonPathableColors) {
357+ if (col == color) {
358+ flags |= MinimapTileNotPathable;
359+ break;
360+ }
361+ }
362+ }
363+
364+ if (c == 255)
365+ continue;
366+
367+ Position pos(topLeft.x + x, topLeft.y + y, topLeft.z);
368+ MinimapBlock &block = getBlock(pos);
369+ Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
370+ MinimapTile &tile =
371+ block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
372+ if (!(tile.flags & MinimapTileWasSeen)) {
373+ tile.color = c;
374+ tile.flags = flags;
375+ block.mustUpdate();
376+ }
377+ }
378+ }
379+ return true;
380+ } catch (stdext::exception &e) {
381+ g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
382+ return false;
383+ }
384+}
385+
386+void Minimap::saveImage(const std::string &fileName, const Rect &mapRect) {
387+ // TODO
388+}
389+
390+bool Minimap::loadOtmm(const std::string &fileName) {
391+ try {
392+ FileStreamPtr fin = g_resources.openFile(
393+ fileName, g_game.getFeature(Otc::GameDontCacheFiles));
394+ if (!fin)
395+ stdext::throw_exception("unable to open file");
396+
397+ uint32 signature = fin->getU32();
398+ if (signature != OTMM_SIGNATURE)
399+ stdext::throw_exception("invalid OTMM file");
400+
401+ uint16 start = fin->getU16();
402+ uint16 version = fin->getU16();
403+ fin->getU32(); // flags
404+
405+ switch (version) {
406+ case 1: {
407+ fin->getString(); // description
408+ break;
409+ }
410+ default:
411+ stdext::throw_exception("OTMM version not supported");
412+ }
413+
414+ fin->seek(start);
415+
416+ uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
417+ std::vector<uchar> compressBuffer(compressBound(blockSize));
418+ std::vector<uchar> decompressBuffer(blockSize);
419+
420+ while (true) {
421+ Position pos;
422+ pos.x = fin->getU16();
423+ pos.y = fin->getU16();
424+ pos.z = fin->getU8();
425+
426+ // end of file or file is corrupted
427+ if (!pos.isValid() || pos.z >= Otc::MAX_Z + 1)
428+ break;
429+
430+ MinimapBlock &block = getBlock(pos);
431+ ulong len = fin->getU16();
432+ ulong destLen = blockSize;
433+ fin->read(compressBuffer.data(), len);
434+ int ret = uncompress(decompressBuffer.data(), &destLen,
435+ compressBuffer.data(), len);
436+ if (ret != Z_OK || destLen != blockSize)
437+ break;
438+
439+ memcpy((uchar *)&block.getTiles(), decompressBuffer.data(), blockSize);
440+ block.mustUpdate();
441+ block.justSaw();
442+ }
443+
444+ fin->close();
445+ return true;
446+ } catch (stdext::exception &e) {
447+ g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
448+ return false;
449+ }
450+}
451+
452+void Minimap::saveOtmm(const std::string &fileName) {
453+ try {
454+ stdext::timer saveTimer;
455+
456+#ifndef ANDROID
457+ std::string tmpFileName = fileName;
458+ tmpFileName += ".tmp";
459+ FileStreamPtr fin = g_resources.createFile(tmpFileName);
460+#else
461+ FileStreamPtr fin = g_resources.createFile(fileName);
462+#endif
463+
464+ // TODO: compression flag with zlib
465+ uint32 flags = 0;
466+
467+ // header
468+ fin->addU32(OTMM_SIGNATURE);
469+ fin->addU16(0); // data start, will be overwritten later
470+ fin->addU16(OTMM_VERSION);
471+ fin->addU32(flags);
472+
473+ // version 1 header
474+ fin->addString("OTMM 1.0"); // description
475+
476+ // go back and rewrite where the map data starts
477+ uint32 start = fin->tell();
478+ fin->seek(4);
479+ fin->addU16(start);
480+ fin->seek(start);
481+
482+ uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile);
483+ std::vector<uchar> compressBuffer(compressBound(blockSize));
484+ const int COMPRESS_LEVEL = 3;
485+
486+ for (uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
487+ for (auto &it : m_tileBlocks[z]) {
488+ int index = it.first;
489+ MinimapBlock &block = *it.second;
490+ if (!block.wasSeen())
491+ continue;
492+
493+ Position pos = getIndexPosition(index, z);
494+ fin->addU16(pos.x);
495+ fin->addU16(pos.y);
496+ fin->addU8(pos.z);
497+
498+ ulong len = blockSize;
499+ int ret =
500+ compress2(compressBuffer.data(), &len, (uchar *)&block.getTiles(),
501+ blockSize, COMPRESS_LEVEL);
502+ VALIDATE(ret == Z_OK);
503+ fin->addU16(len);
504+ fin->write(compressBuffer.data(), len);
505+ }
506+ }
507+
508+ // end of file
509+ Position invalidPos;
510+ fin->addU16(invalidPos.x);
511+ fin->addU16(invalidPos.y);
512+ fin->addU8(invalidPos.z);
513+
514+ fin->flush();
515+
516+ fin->close();
517+#ifndef ANDROID
518+ std::filesystem::path filePath(g_resources.getWriteDir()),
519+ tmpFilePath(g_resources.getWriteDir());
520+ filePath += fileName;
521+ tmpFilePath += tmpFileName;
522+ if (std::filesystem::file_size(tmpFilePath) > 1024) {
523+ std::filesystem::rename(tmpFilePath, filePath);
524+ }
525+#endif
526+ } catch (stdext::exception &e) {
527+ g_logger.error(stdext::format("failed to save OTMM minimap: %s", e.what()));
528+ } catch (std::exception &e) {
529+ g_logger.error(stdext::format("failed to save OTMM minimap: %s", e.what()));
530+ }
531+}
A RD_Client/src/client/animatedtext.cpp +106-0 View file
@@ -0,0 +1,106 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "animatedtext.h"
24+#include "map.h"
25+#include "game.h"
26+#include <framework/core/clock.h>
27+#include <framework/core/eventdispatcher.h>
28+#include <framework/graphics/graphics.h>
29+
30+AnimatedText::AnimatedText()
31+{
32+ m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded"));
33+ m_cachedText.setAlign(Fw::AlignLeft);
34+}
35+
36+void AnimatedText::drawText(const Point& dest, const Rect& visibleRect)
37+{
38+ static float tf = Otc::ANIMATED_TEXT_DURATION;
39+ static float tftf = Otc::ANIMATED_TEXT_DURATION * Otc::ANIMATED_TEXT_DURATION;
40+
41+ Point p = dest;
42+ Size textSize = m_cachedText.getTextSize();
43+ float t = m_animationTimer.ticksElapsed();
44+ p.x -= textSize.width() / 2;
45+
46+ if(g_game.getFeature(Otc::GameDiagonalAnimatedText)) {
47+ p.x -= (4 * t / tf) + (8 * t * t / tftf);
48+ }
49+
50+ p.y += (-48 * t) / tf;
51+ p += m_offset;
52+ Rect rect(p, textSize);
53+
54+ if(visibleRect.contains(rect)) {
55+ float t0 = tf / 1.2;
56+ Color color = m_color;
57+ if(t > t0) {
58+ color.setAlpha((float)(1 - (t - t0) / (tf - t0)));
59+ }
60+ m_cachedText.draw(rect, color);
61+ }
62+}
63+
64+void AnimatedText::onAppear()
65+{
66+ m_animationTimer.restart();
67+
68+ // schedule removal
69+ auto self = asAnimatedText();
70+ g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::ANIMATED_TEXT_DURATION);
71+}
72+
73+void AnimatedText::setColor(int color)
74+{
75+ m_color = Color::from8bit(color);
76+}
77+
78+void AnimatedText::setText(const std::string& text)
79+{
80+ m_cachedText.setText(text);
81+}
82+
83+void AnimatedText::setFont(const std::string& fontName)
84+{
85+ m_cachedText.setFont(g_fonts.getFont(fontName));
86+}
87+
88+bool AnimatedText::merge(const AnimatedTextPtr& other)
89+{
90+ if(other->getColor() != m_color)
91+ return false;
92+
93+ if(other->getCachedText().getFont() != m_cachedText.getFont())
94+ return false;
95+
96+ if(m_animationTimer.ticksElapsed() > (int)Otc::ANIMATED_TEXT_DURATION / 2.5)
97+ return false;
98+
99+ try {
100+ int number = stdext::safe_cast<int>(m_cachedText.getText());
101+ int otherNumber = stdext::safe_cast<int>(other->getCachedText().getText());
102+ m_cachedText.setText(std::to_string(number + otherNumber));
103+ return true;
104+ } catch(...) {}
105+ return false;
106+}
A RD_Client/src/client/animatedtext.h +65-0 View file
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef ANIMATEDTEXT_H
24+#define ANIMATEDTEXT_H
25+
26+#include "thing.h"
27+#include <framework/graphics/fontmanager.h>
28+#include <framework/core/timer.h>
29+#include <framework/graphics/cachedtext.h>
30+
31+// @bindclass
32+class AnimatedText : public Thing
33+{
34+public:
35+ AnimatedText();
36+
37+ void drawText(const Point& dest, const Rect& visibleRect);
38+
39+ void setColor(int color);
40+ void setText(const std::string& text);
41+ void setOffset(const Point& offset) { m_offset = offset; }
42+ void setFont(const std::string& fontName);
43+
44+ Color getColor() { return m_color; }
45+ const CachedText& getCachedText() const { return m_cachedText; }
46+ Point getOffset() { return m_offset; }
47+ Timer getTimer() { return m_animationTimer; }
48+
49+ bool merge(const AnimatedTextPtr& other);
50+
51+ AnimatedTextPtr asAnimatedText() { return static_self_cast<AnimatedText>(); }
52+ bool isAnimatedText() { return true; }
53+ std::string getText() { return m_cachedText.getText(); }
54+
55+protected:
56+ virtual void onAppear();
57+
58+private:
59+ Color m_color;
60+ Timer m_animationTimer;
61+ CachedText m_cachedText;
62+ Point m_offset;
63+};
64+
65+#endif
A RD_Client/src/client/animator.cpp +240-0 View file
@@ -0,0 +1,240 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "animator.h"
24+#include "declarations.h"
25+
26+#include <framework/core/clock.h>
27+#include <framework/core/filestream.h>
28+#include <framework/stdext/fastrand.h>
29+#include <framework/util/extras.h>
30+
31+Animator::Animator() {
32+ m_animationPhases = 0;
33+ m_startPhase = 0;
34+ m_loopCount = 0;
35+ m_async = false;
36+ m_currentDuration = 0;
37+ m_currentDirection = AnimDirForward;
38+ m_currentLoop = 0;
39+ m_lastPhaseTicks = 0;
40+ m_isComplete = false;
41+ m_phase = 0;
42+}
43+
44+void Animator::unserialize(int animationPhases, const FileStreamPtr &fin) {
45+ m_animationPhases = animationPhases;
46+ m_async = fin->getU8() == 0;
47+ m_loopCount = fin->get32();
48+ m_startPhase = fin->get8();
49+
50+ for (int i = 0; i < m_animationPhases; ++i) {
51+ int minimum = fin->getU32();
52+ int maximum = fin->getU32();
53+ m_phaseDurations.push_back(
54+ std::make_pair(minimum, std::max(0, maximum - minimum)));
55+ }
56+
57+ m_phase = getStartPhase();
58+
59+ VALIDATE(m_animationPhases == (int)m_phaseDurations.size());
60+ VALIDATE(m_startPhase >= -1 && m_startPhase < m_animationPhases);
61+}
62+
63+void Animator::serialize(const FileStreamPtr &fin) {
64+ fin->addU8(m_async ? 0 : 1);
65+ fin->add32(m_loopCount);
66+ fin->add8(m_startPhase);
67+
68+ for (auto &phase : m_phaseDurations) {
69+ fin->addU32(phase.first);
70+ fin->addU32(phase.first + phase.second);
71+ }
72+}
73+
74+void Animator::setPhase(int phase) {
75+ if (m_phase == phase && !m_isComplete)
76+ return;
77+
78+ if (m_async) {
79+ if (phase == AnimPhaseAsync)
80+ m_phase = 0;
81+ else if (phase == AnimPhaseRandom)
82+ m_phase = (int)stdext::random_range(0, (long)m_animationPhases);
83+ else if (phase >= 0 && phase < m_animationPhases)
84+ m_phase = phase;
85+ else
86+ m_phase = getStartPhase();
87+
88+ m_isComplete = false;
89+ m_lastPhaseTicks = g_clock.millis();
90+ m_currentDuration = getPhaseDuration(phase);
91+ m_currentLoop = 0;
92+ } else
93+ calculateSynchronous();
94+}
95+
96+int Animator::getPhase() {
97+ ticks_t ticks = g_clock.millis();
98+ if (ticks != m_lastPhaseTicks && !m_isComplete) {
99+ int elapsedTicks = (int)(ticks - m_lastPhaseTicks);
100+ if (elapsedTicks >= m_currentDuration) {
101+ int phase = m_phase;
102+ while (elapsedTicks >= m_currentDuration) {
103+ elapsedTicks -= m_currentDuration;
104+ if (m_loopCount < 0)
105+ phase = getPingPongPhase();
106+ else
107+ phase = getLoopPhase();
108+
109+ if (m_phase != phase) {
110+ m_phase = phase;
111+ m_currentDuration = getPhaseDuration(phase);
112+ } else {
113+ m_isComplete = true;
114+ break;
115+ }
116+ }
117+ if (!m_isComplete)
118+ m_currentDuration -= elapsedTicks;
119+ } else {
120+ m_currentDuration -= elapsedTicks;
121+ }
122+ m_lastPhaseTicks = ticks;
123+ }
124+ return m_phase;
125+}
126+
127+int Animator::getPhaseAt(Timer &timer, int lastPhase) {
128+ static int rand_val = 6;
129+ ticks_t time = timer.ticksElapsed();
130+ for (int i = lastPhase; i < m_animationPhases; ++i) {
131+ int phaseDuration = m_phaseDurations[i].second == 0
132+ ? m_phaseDurations[i].first
133+ : m_phaseDurations[i].first +
134+ rand_val % (m_phaseDurations[i].second);
135+ rand_val = rand_val * 7 + 11;
136+ if (time < phaseDuration) {
137+ timer.restart();
138+ timer.adjust(-time);
139+ return i;
140+ }
141+ time -= phaseDuration;
142+ }
143+ return 0;
144+ /*
145+ ticks_t total = 0;
146+
147+ for (const auto &pair : m_phaseDurations) {
148+ total += std::get<1>(pair);
149+
150+ if (time < total) {
151+ return index;
152+ }
153+
154+ ++index;
155+ }
156+
157+ return std::min<int>(index, m_animationPhases - 1);
158+ */
159+}
160+
161+int Animator::getStartPhase() {
162+ if (m_startPhase > -1)
163+ return m_startPhase;
164+ return (int)stdext::random_range(0, (long)m_animationPhases);
165+}
166+
167+void Animator::resetAnimation() {
168+ m_isComplete = false;
169+ m_currentDirection = AnimDirForward;
170+ m_currentLoop = 0;
171+ setPhase(AnimPhaseAutomatic);
172+}
173+
174+int Animator::getPingPongPhase() {
175+ int count = m_currentDirection == AnimDirForward ? 1 : -1;
176+ int nextPhase = m_phase + count;
177+ if (nextPhase < 0 || nextPhase >= m_animationPhases) {
178+ m_currentDirection =
179+ m_currentDirection == AnimDirForward ? AnimDirBackward : AnimDirForward;
180+ count *= -1;
181+ }
182+ return m_phase + count;
183+}
184+
185+int Animator::getLoopPhase() {
186+ int nextPhase = m_phase + 1;
187+ if (nextPhase < m_animationPhases)
188+ return nextPhase;
189+
190+ if (m_loopCount == 0)
191+ return 0;
192+
193+ if (m_currentLoop < (m_loopCount - 1)) {
194+ m_currentLoop++;
195+ return 0;
196+ }
197+
198+ return m_phase;
199+}
200+
201+int Animator::getPhaseDuration(int phase) {
202+ VALIDATE(phase < (int)m_phaseDurations.size());
203+
204+ auto &data = m_phaseDurations.at(phase);
205+ if (data.second == 0)
206+ return data.first;
207+ int min = data.first;
208+ int max = min + data.second;
209+ return (int)stdext::random_range((long)min, (long)max);
210+}
211+
212+void Animator::calculateSynchronous() {
213+ m_isComplete = false;
214+ int totalDuration = 0;
215+ for (int i = 0; i < m_animationPhases; i++)
216+ totalDuration += getPhaseDuration(i);
217+
218+ ticks_t ticks = g_clock.millis();
219+ int elapsedTicks = (int)(ticks % totalDuration);
220+ int totalTime = 0;
221+ for (int i = 0; i < m_animationPhases; i++) {
222+ int duration = getPhaseDuration(i);
223+ if (elapsedTicks >= totalTime && elapsedTicks < totalTime + duration) {
224+ m_phase = i;
225+ m_currentDuration = duration - (elapsedTicks - totalTime);
226+ break;
227+ }
228+ totalTime += duration;
229+ }
230+ m_lastPhaseTicks = ticks;
231+}
232+
233+ticks_t Animator::getTotalDuration() {
234+ ticks_t time = 0;
235+ for (const auto &pair : m_phaseDurations) {
236+ time += pair.first + pair.second;
237+ }
238+
239+ return time;
240+}
A RD_Client/src/client/animator.h +87-0 View file
@@ -0,0 +1,87 @@
1+/*
2+* Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+*
4+* Permission is hereby granted, free of charge, to any person obtaining a copy
5+* of this software and associated documentation files (the "Software"), to deal
6+* in the Software without restriction, including without limitation the rights
7+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+* copies of the Software, and to permit persons to whom the Software is
9+* furnished to do so, subject to the following conditions:
10+*
11+* The above copyright notice and this permission notice shall be included in
12+* all copies or substantial portions of the Software.
13+*
14+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+* THE SOFTWARE.
21+*/
22+
23+#ifndef ANIMATOR_H
24+#define ANIMATOR_H
25+
26+#include "declarations.h"
27+
28+#include <framework/core/declarations.h>
29+#include <framework/core/timer.h>
30+
31+enum AnimationPhase : int16
32+{
33+ AnimPhaseAutomatic = -1,
34+ AnimPhaseRandom = 254,
35+ AnimPhaseAsync = 255,
36+};
37+
38+enum AnimationDirection : uint8
39+{
40+ AnimDirForward = 0,
41+ AnimDirBackward = 1
42+};
43+
44+class Animator : public stdext::shared_object
45+{
46+public:
47+ Animator();
48+
49+ void unserialize(int animationPhases, const FileStreamPtr& fin);
50+ void serialize(const FileStreamPtr& fin);
51+
52+ void setPhase(int phase);
53+ int getPhase();
54+ int getPhaseAt(Timer& timer, int lastPhase = 0);
55+
56+ int getStartPhase();
57+ int getAnimationPhases() { return m_animationPhases; }
58+ bool isAsync() { return m_async; }
59+ bool isComplete() { return m_isComplete; }
60+
61+ ticks_t getTotalDuration();
62+
63+ void resetAnimation();
64+
65+private:
66+ int getPingPongPhase();
67+ int getLoopPhase();
68+ int getPhaseDuration(int phase);
69+ void calculateSynchronous();
70+
71+ int m_animationPhases;
72+ int m_startPhase;
73+ int m_loopCount;
74+ bool m_async;
75+ std::vector< std::pair<int, int> > m_phaseDurations;
76+
77+ int m_currentDuration;
78+ AnimationDirection m_currentDirection;
79+ int m_currentLoop;
80+
81+ ticks_t m_lastPhaseTicks;
82+ bool m_isComplete;
83+
84+ int m_phase;
85+};
86+
87+#endif
A RD_Client/src/client/client.cpp +60-0 View file
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "client.h"
24+#include <framework/core/modulemanager.h>
25+#include <framework/core/resourcemanager.h>
26+#include <framework/graphics/graphics.h>
27+#include <framework/graphics/shadermanager.h>
28+#include "game.h"
29+#include "map.h"
30+#include "spritemanager.h"
31+#include "minimap.h"
32+#include "healthbars.h"
33+#include <framework/core/configmanager.h>
34+
35+Client g_client;
36+
37+void Client::init(std::vector<std::string>& args)
38+{
39+ // register needed lua functions
40+ registerLuaFunctions();
41+
42+ g_map.init();
43+ g_minimap.init();
44+ g_game.init();
45+ g_shaders.init();
46+ g_things.init();
47+ g_healthBars.init();
48+}
49+
50+void Client::terminate()
51+{
52+ g_creatures.terminate();
53+ g_game.terminate();
54+ g_map.terminate();
55+ g_minimap.terminate();
56+ g_things.terminate();
57+ g_sprites.terminate();
58+ g_shaders.terminate();
59+ g_healthBars.terminate();
60+}
A RD_Client/src/client/client.h +38-0 View file
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef CLIENT_H
24+#define CLIENT_H
25+
26+#include "global.h"
27+
28+class Client
29+{
30+public:
31+ void init(std::vector<std::string>& args);
32+ void terminate();
33+ void registerLuaFunctions();
34+};
35+
36+extern Client g_client;
37+
38+#endif
A RD_Client/src/client/CMakeLists.txt +99-0 View file
@@ -0,0 +1,99 @@
1+add_definitions(-DCLIENT)
2+
3+set(client_SOURCES ${client_SOURCES}
4+ # client
5+ ${CMAKE_CURRENT_LIST_DIR}/const.h
6+ ${CMAKE_CURRENT_LIST_DIR}/global.h
7+ ${CMAKE_CURRENT_LIST_DIR}/luafunctions_client.cpp
8+ ${CMAKE_CURRENT_LIST_DIR}/client.cpp
9+ ${CMAKE_CURRENT_LIST_DIR}/client.h
10+
11+ # core
12+ ${CMAKE_CURRENT_LIST_DIR}/animatedtext.cpp
13+ ${CMAKE_CURRENT_LIST_DIR}/animatedtext.h
14+ ${CMAKE_CURRENT_LIST_DIR}/animator.h
15+ ${CMAKE_CURRENT_LIST_DIR}/animator.cpp
16+ ${CMAKE_CURRENT_LIST_DIR}/container.cpp
17+ ${CMAKE_CURRENT_LIST_DIR}/container.h
18+ ${CMAKE_CURRENT_LIST_DIR}/creature.cpp
19+ ${CMAKE_CURRENT_LIST_DIR}/creature.h
20+ ${CMAKE_CURRENT_LIST_DIR}/declarations.h
21+ ${CMAKE_CURRENT_LIST_DIR}/effect.cpp
22+ ${CMAKE_CURRENT_LIST_DIR}/effect.h
23+ ${CMAKE_CURRENT_LIST_DIR}/game.cpp
24+ ${CMAKE_CURRENT_LIST_DIR}/game.h
25+ ${CMAKE_CURRENT_LIST_DIR}/item.cpp
26+ ${CMAKE_CURRENT_LIST_DIR}/item.h
27+ ${CMAKE_CURRENT_LIST_DIR}/localplayer.cpp
28+ ${CMAKE_CURRENT_LIST_DIR}/localplayer.h
29+ ${CMAKE_CURRENT_LIST_DIR}/map.cpp
30+ ${CMAKE_CURRENT_LIST_DIR}/map.h
31+ ${CMAKE_CURRENT_LIST_DIR}/mapio.cpp
32+ ${CMAKE_CURRENT_LIST_DIR}/mapview.cpp
33+ ${CMAKE_CURRENT_LIST_DIR}/mapview.h
34+ ${CMAKE_CURRENT_LIST_DIR}/minimap.cpp
35+ ${CMAKE_CURRENT_LIST_DIR}/minimap.h
36+ ${CMAKE_CURRENT_LIST_DIR}/lightview.cpp
37+ ${CMAKE_CURRENT_LIST_DIR}/lightview.h
38+ ${CMAKE_CURRENT_LIST_DIR}/missile.cpp
39+ ${CMAKE_CURRENT_LIST_DIR}/missile.h
40+ ${CMAKE_CURRENT_LIST_DIR}/outfit.cpp
41+ ${CMAKE_CURRENT_LIST_DIR}/outfit.h
42+ ${CMAKE_CURRENT_LIST_DIR}/player.cpp
43+ ${CMAKE_CURRENT_LIST_DIR}/player.h
44+ ${CMAKE_CURRENT_LIST_DIR}/spritemanager.cpp
45+ ${CMAKE_CURRENT_LIST_DIR}/spritemanager.h
46+ ${CMAKE_CURRENT_LIST_DIR}/statictext.cpp
47+ ${CMAKE_CURRENT_LIST_DIR}/statictext.h
48+ ${CMAKE_CURRENT_LIST_DIR}/thing.cpp
49+ ${CMAKE_CURRENT_LIST_DIR}/thing.h
50+ ${CMAKE_CURRENT_LIST_DIR}/thingtypemanager.cpp
51+ ${CMAKE_CURRENT_LIST_DIR}/thingtypemanager.h
52+ ${CMAKE_CURRENT_LIST_DIR}/thingtype.cpp
53+ ${CMAKE_CURRENT_LIST_DIR}/thingtype.h
54+ ${CMAKE_CURRENT_LIST_DIR}/itemtype.cpp
55+ ${CMAKE_CURRENT_LIST_DIR}/itemtype.h
56+ ${CMAKE_CURRENT_LIST_DIR}/tile.cpp
57+ ${CMAKE_CURRENT_LIST_DIR}/tile.h
58+ ${CMAKE_CURRENT_LIST_DIR}/healthbars.cpp
59+ ${CMAKE_CURRENT_LIST_DIR}/healthbars.h
60+ ${CMAKE_CURRENT_LIST_DIR}/houses.cpp
61+ ${CMAKE_CURRENT_LIST_DIR}/houses.h
62+ ${CMAKE_CURRENT_LIST_DIR}/towns.cpp
63+ ${CMAKE_CURRENT_LIST_DIR}/towns.h
64+ ${CMAKE_CURRENT_LIST_DIR}/creatures.cpp
65+ ${CMAKE_CURRENT_LIST_DIR}/creatures.h
66+
67+ # lua
68+ ${CMAKE_CURRENT_LIST_DIR}/luavaluecasts_client.cpp
69+ ${CMAKE_CURRENT_LIST_DIR}/luavaluecasts_client.h
70+
71+ # net
72+ ${CMAKE_CURRENT_LIST_DIR}/protocolcodes.cpp
73+ ${CMAKE_CURRENT_LIST_DIR}/protocolcodes.h
74+ ${CMAKE_CURRENT_LIST_DIR}/protocolgame.cpp
75+ ${CMAKE_CURRENT_LIST_DIR}/protocolgame.h
76+ ${CMAKE_CURRENT_LIST_DIR}/protocolgameparse.cpp
77+ ${CMAKE_CURRENT_LIST_DIR}/protocolgamesend.cpp
78+
79+ # ui
80+ ${CMAKE_CURRENT_LIST_DIR}/uicreature.cpp
81+ ${CMAKE_CURRENT_LIST_DIR}/uicreature.h
82+ ${CMAKE_CURRENT_LIST_DIR}/uiitem.cpp
83+ ${CMAKE_CURRENT_LIST_DIR}/uiitem.h
84+ ${CMAKE_CURRENT_LIST_DIR}/uigraph.cpp
85+ ${CMAKE_CURRENT_LIST_DIR}/uigraph.h
86+ ${CMAKE_CURRENT_LIST_DIR}/uimap.cpp
87+ ${CMAKE_CURRENT_LIST_DIR}/uimap.h
88+ ${CMAKE_CURRENT_LIST_DIR}/uiminimap.cpp
89+ ${CMAKE_CURRENT_LIST_DIR}/uiminimap.h
90+ ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.cpp
91+ ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.h
92+ ${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.cpp
93+ ${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.h
94+ ${CMAKE_CURRENT_LIST_DIR}/uisprite.cpp
95+ ${CMAKE_CURRENT_LIST_DIR}/uisprite.h
96+
97+ # util
98+ ${CMAKE_CURRENT_LIST_DIR}/position.h
99+)
Top