/[drupal]/contributions/modules/rpg/rpg.module
ViewVC logotype

Contents of /contributions/modules/rpg/rpg.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.60 - (show annotations) (download) (as text)
Sat Apr 26 01:16:29 2008 UTC (19 months ago) by aaron
Branch: MAIN
CVS Tags: HEAD
Changes since 1.59: +1 -2 lines
File MIME type: text/x-php
allow hook_rpg_pc_types to know calling user account
1 <?php
2 // $Id: rpg.module,v 1.59 2008/04/26 01:03:25 aaron Exp $
3
4 /**
5 * @mainpage RPG
6 * This module provides a full-featured game engine for Drupal. As the name implies, it may be used to create
7 * an RPG, but may be used for other types of games as well. Various rulesets exist, which may be imported into
8 * the site to give a base to start from, but a new ruleset may be started from scratch if desired.
9 */
10
11 /**
12 * @file
13 * This module provides the core engine for RPG. It depends on many include files, so has any functions that may be
14 * called directly from a menu item, and the basic functions that should be used when directly accessing the system,
15 * such as rpg_types, rpg_get, rpg_set, etc.
16 */
17
18 define('RPG_ACTION_ROUND_DURATION', 1); // default duration of a game turn in real time seconds
19 define('RPG_ACTION_ROUND_CALENDAR_INCREMENT', 0); // default seconds the default game calendar is incremented in a game turn
20 define('RPG_MAX_ACTION_POINTS', 120); // default number of action points a character has until they must be recharged
21 define('RPG_ACTION_RECHARGE_RATE', 720); // default number of rounds that must pass before an action point is restored
22
23 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.theme.inc');
24
25 /**
26 * implement hook_menu
27 *
28 * As this function is rarely called, and is a relatively large function, we've moved this to another file.
29 * I believe this will be the de facto standard in D6 anyway, makes this an early adopter, I guess....
30 *
31 * @see _rpg_menu_cache()
32 */
33 function rpg_menu($may_cache) {
34 $items = array();
35
36 if ($may_cache) {
37 // include _rpg_menu_cache -- this function defines all the defined menu items
38 rpg_include_admin();
39 $items = _rpg_menu_cache();
40 }
41 else {
42 // determine things like active char, etc.
43 // TODO: move this to a better place to always get called once per page load
44 // i think it's changing in drupal 6
45 _rpg_set_globals();
46 }
47 return $items;
48 }
49
50 /**
51 * implement hook_perm
52 * TODO: move this description to an api file
53 * 'play rpg' => users may play the rpg
54 * 'create pc rpg object' => users may create a new pc to play in the rpg. this may be limited by certain hooks
55 * 'administer rpg' => users may administer any aspect of the game, including game settings
56 * 'create rpg object' => users may create new rpg objects. this may be limited by certain hooks
57 * 'edit own pc rpg object' => users may edit their own pc object
58 * 'edit own rpg object' => users may edit their own rpg objects (except the pc object)
59 * 'edit pc rpg object' => users may edit any pc object
60 * 'edit rpg object' => users may edit any rpg objects, except other pcs
61 * 'super in-game rpg' => users have access to special in-game powers, such as teleporting and viewing others' stats,
62 * but may not necessarily edit overall game settings (which is in the domain of 'administer rpg')
63 */
64 function rpg_perm() {
65 return array('play rpg', 'view rpg objects', 'create pc rpg object', 'administer rpg', 'create rpg object', 'edit own pc rpg object', 'edit own rpg object', 'edit rpg object', 'super rpg');
66 }
67
68 /**
69 * Is the character (as controlled by the user) allowed to view a specific object? In general, this is called by
70 * rpg/view to determine if an object may be seen. Assuming the user has perms, the character will usually only be
71 * able to view the object if in the same room as it, or similar circumstances.
72 *
73 * calls hook_rpg_view_access, which returns FALSE or TRUE. Any modules that return FALSE will deny access, trumping the others.
74 */
75 function rpg_view_access($who = NULL, $object = NULL) {
76 if (user_access('administer rpg')) { return TRUE; }
77 if (!user_access('view rpg objects')) { return FALSE; }
78 if (is_null($who)) {
79 global $rpg;
80 $who = $rpg['pc'];
81 }
82 foreach (module_invoke_all('rpg_view_access', $who, $object) as $access) {
83 if ($access === FALSE) {
84 return FALSE;
85 }
86 }
87 return TRUE;
88 }
89
90 /**
91 * Is the character (as controlled by the user) allowed to edit a specific object? In general, this is called by
92 * rpg/edit to determine if an object is owned by a user. Assuming the user has perms, the character will usually only be
93 * able to edit the object if he created it, or similar circumstances.
94 *
95 * calls hook_rpg_edit_access, which returns FALSE or TRUE. Any modules that return FALSE will deny access, trumping the others.
96 */
97 function rpg_edit_access($object = NULL) {
98 if (user_access('administer rpg') || user_access('edit rpg object')) { return TRUE; }
99 global $user;
100 $object = rpg_object($object);
101 if ($object->uid == $user->uid) {
102 return TRUE;
103 }
104 return FALSE;
105 }
106
107 function rpg_reset_cache() {
108 db_query("DELETE FROM {cache_rpg}");
109 }
110
111 /**
112 * make sure we save any changes to rpg objects
113 */
114 function rpg_exit() {
115 _rpg_save_all_marked_objects();
116 }
117
118 /**
119 * this initializes the global $rpg array and loads some included core rpg files.
120 * it also sets other rpg global variables, such as active character
121 * @param $reset
122 * if set to true, this will reset the global $rpg array (after first saving any changes)
123 */
124 function _rpg_set_globals($reset = false) {
125 global $rpg, $user;
126 static $loaded;
127
128 if ($loaded && !$reset) {
129 return;
130 }
131
132 // load functions that are used internally, but only when accessing an rpg page
133 // TODO: remove these and put the calls in proper places
134 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.db.inc');
135 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.invoke.inc');
136 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.t.inc');
137
138 // if we've previously loaded any objects, make sure to save any changes before resetting our global array
139 if (is_array($rpg)) {
140 rpg_include_db();
141 _rpg_save_all_marked_objects();
142 }
143
144 // initialize our global array of rpg objects
145 $rpg = array();
146
147 // if we're logged in, get the active pc (if any)
148 if ($user->uid && user_access('play rpg')) {
149 $sql = "SELECT rid FROM {rpg_pc} WHERE uid=%d AND active=1";
150 $rid = db_result(db_query($sql, $user->uid));
151 if ($rid) {
152 $rpg['pc'] = $rid;
153 }
154 }
155 $loaded = true;
156 }
157
158 // callback for the game admin page menu item
159 function rpg_admin_game() {
160 rpg_include_admin();
161 $form = _rpg_admin_game();
162 return system_settings_form($form);
163 }
164
165 /**
166 * FAPI definition for the RPG Type admin screen callback. Called with /admin/rpg/types
167 *
168 * This is the rpg type admin screen callback. Technically not a form, although it is called as one. This page
169 * displays a table outlining the various types available, with several actions available for each type from the screen,
170 * such as edit, delete, attributes, and actions.
171 *
172 * @ingroup forms
173 * @ingroup types
174 *
175 * @see theme_rpg_admin_types
176 */
177 function rpg_admin_types() {
178 $types = rpg_types(NULL, false);
179 return theme('rpg_admin_types', $types);
180 }
181
182 /**
183 * FAPI definition for the 'Add RPG Type' form. Called with /admin/rpg/types/type/add
184 *
185 * This defines the screen used by admins to add a new RPG type. All objects are defined from types, and types may
186 * inherit from one or more types. Each type will have its own set of attributes and actions, which are also
187 * inherited, and may be overridden.
188 *
189 * @ingroup forms
190 * @ingroup types
191 *
192 * @see _rpg_admin_type_form()
193 * @see rpg_admin_type_add_validate()
194 * @see rpg_admin_type_add_submit()
195 */
196 function rpg_admin_type_add() {
197 rpg_include_admin();
198 $type = rpg_new_type();
199 $form = _rpg_admin_type_form($type);
200 $form['new'] = array(
201 '#type' => 'value',
202 '#value' => true,
203 );
204 return $form;
205 }
206
207 /**
208 * FAPI definition for the 'Edit RPG Type' form. Called with /admin/rpg/types/type/[TYPE]/edit
209 *
210 * This defines the screen used by admins to edit an existing RPG type. All objects are defined from types, and types may
211 * inherit from one or more types. Each type will have its own set of attributes and actions, which are also
212 * inherited, and may be overridden.
213 *
214 * @param $type
215 * The string of the type to edit, such as 'tangible' or 'actor'. Called as arg(4) in the url above.
216 *
217 * @ingroup forms
218 * @ingroup types
219 *
220 * @see _rpg_admin_type_form()
221 * @see rpg_admin_type_edit_validate()
222 * @see rpg_admin_type_edit_submit()
223 */
224 function rpg_admin_type_edit($type) {
225 rpg_include_admin();
226 $type = rpg_types($type, false);
227 if (!$type['type']) {
228 drupal_not_found();
229 }
230 $form = _rpg_admin_type_form($type);
231 return $form;
232 }
233
234 /**
235 * Create an empty type array.
236 *
237 * Called when adding a new type, whether programatically or from a form. It is not added to the database yet.
238 *
239 * @return
240 * An empty array with key values for each of the expected values.
241 */
242 function rpg_new_type() {
243 $type = array();
244 $type['type'] = '';
245 $type['name'] = '';
246 $type['parents'] = array();
247 $type['shortdesc'] = '';
248 $type['longdesc'] = '';
249 return $type;
250 }
251
252 /**
253 * validate callback for the 'add type' form
254 */
255 function rpg_admin_type_add_validate($form_id, $form_values) {
256 $type = rpg_types($form_values['type']);
257 // if someone's already added the type we're trying to add...
258 if ($type['type']) {
259 form_set_error('type', t('The %type type has already been defined. Please choose another machine-readable name.', array('%type' => $type['type'])));
260 }
261 }
262
263 /**
264 * submit callback for the 'add type' form
265 */
266 function rpg_admin_type_add_submit($form_id, $form_values) {
267 $type['type'] = $form_values['type'];
268 $type['name'] = $form_values['name'];
269 $type['parents'] = $form_values['parents'];
270 $type['shortdesc'] = $form_values['shortdesc'];
271 $type['longdesc'] = $form_values['longdesc'];
272
273 // rpg_save_type is in an external include file
274 rpg_include_loadcache();
275 // save our new type
276 rpg_save_type($type);
277
278 drupal_set_message(t('Your changes to the object type have been saved.'));
279
280 return 'admin/rpg/types/type/' . $type['type'];
281 }
282
283 /**
284 * validate callback for the 'edit type' form
285 */
286 function rpg_admin_type_edit_validate($form_id, $form_values) {
287 $type = rpg_types($form_values['type']);
288 // if type type we're editing has been deleted since we began editing...
289 if (!$type['type']) {
290 form_set_error('type', t('There was an error in processing your form; that type is no longer in the database.'));
291 }
292 }
293
294 /**
295 * submit callback for the 'edit type' form
296 */
297 function rpg_admin_type_edit_submit($form_id, $form_values) {
298 $type = rpg_types($form_values['type']);
299
300 $type['name'] = $form_values['name'];
301 $type['parents'] = $form_values['parents'];
302 $type['shortdesc'] = $form_values['shortdesc'];
303 $type['longdesc'] = $form_values['longdesc'];
304
305 // rpg_save_type is in an external include file
306 rpg_include_loadcache();
307 rpg_save_type($type);
308
309 drupal_set_message(t('Your changes to the object type have been saved.'));
310 }
311
312 function rpg_admin_type_attributes_form($type) {
313 $type = rpg_types($type);
314 if (!$type['type']) {
315 drupal_not_found();
316 }
317
318 drupal_set_title(t('Attributes for @type', array('@type' => $type['name'])));
319
320 // include the include file w/ our helper function
321 rpg_include_admin();
322
323 return _rpg_admin_type_attributes_form($type);
324 }
325
326 /**
327 * FAPI definition for the 'Add new RPG Attribute' form. Called with /admin/rpg/types/type/[TYPE]/attributes/add/[[ATTRIBUTE]]
328 *
329 * This defines the screen used by admins to add an attribute for an existing RPG type. If called with an optional
330 * $attribute string of an existing attribute defined by a parent of the type, then this screen defines an overridden
331 * attribute.
332 *
333 * @param $type
334 * The string of the type to edit, such as 'tangible' or 'actor'. Called as arg(4) in the url above.
335 * @param $attribute
336 * If given (as the last arg), then it's a string of the attribute to override. Otherwise, it will add a new attribute.
337 *
338 * @ingroup forms
339 * @ingroup attributes
340 *
341 * @see _rpg_admin_type_attribute_add()
342 * @see rpg_admin_type_attribute_add_validate()
343 * @see rpg_admin_type_attribute_add_submit()
344 */
345 function rpg_admin_type_attribute_add($type, $attribute = NULL) {
346 $type = rpg_types($type);
347 if (!$type['type']) {
348 drupal_not_found();
349 }
350
351 // include the include file w/ our helper function
352 rpg_include_admin();
353
354 return _rpg_admin_type_attribute_add($type, $attribute);
355 }
356
357 /**
358 * FAPI definition for the 'Add new RPG Action' form. Called with /admin/rpg/types/type/[TYPE]/actions/add/[[ACTION]]
359 *
360 * This defines the screen used by admins to add an action for an existing RPG type. If called with an optional
361 * $action string of an existing action defined by a parent of the type, then this screen defines an overridden
362 * action.
363 *
364 * @param $type
365 * The string of the type to edit, such as 'tangible' or 'actor'. Called as arg(4) in the url above.
366 * @param $action
367 * If given (as the last arg), then it's a string of the action to override. Otherwise, it will add a new action.
368 *
369 * @ingroup forms
370 * @ingroup attributes
371 *
372 * @see _rpg_admin_type_action_add()
373 * @see rpg_admin_type_action_add_validate()
374 * @see rpg_admin_type_action_add_submit()
375 */
376 function rpg_admin_type_action_add($type, $action = NULL) {
377 $type = rpg_types($type);
378 if (!$type['type']) {
379 drupal_not_found();
380 }
381
382 // include the include file w/ our helper function
383 rpg_include_admin();
384
385 return _rpg_admin_type_action_add($type, $action);
386 }
387
388 /**
389 * FAPI definition for the 'Add new RPG Event' form. Called with /admin/rpg/types/type/[TYPE]/events/add/[[ACTION]]
390 *
391 * This defines the screen used by admins to add an event for an existing RPG type. If called with an optional
392 * $event string of an existing event defined by a parent of the type, then this screen defines an overridden
393 * event.
394 *
395 * @param $type
396 * The string of the type to edit, such as 'tangible' or 'actor'. Called as arg(4) in the url above.
397 * @param $event
398 * If given (as the last arg), then it's a string of the event to override. Otherwise, it will add a new event.
399 *
400 * @ingroup forms
401 * @ingroup attributes
402 *
403 * @see _rpg_admin_type_event_add()
404 * @see rpg_admin_type_event_add_validate()
405 * @see rpg_admin_type_event_add_submit()
406 */
407 function rpg_admin_type_event_add($type, $event = NULL) {
408 $type = rpg_types($type);
409 if (!$type['type']) {
410 drupal_not_found();
411 }
412
413 // include the include file w/ our helper function
414 rpg_include_admin();
415
416 return _rpg_admin_type_event_add($type, $event);
417 }
418
419 function rpg_admin_type_attribute_edit($type, $attribute) {
420 $type = rpg_types($type);
421 $attribute = rpg_attributes($attribute);
422 if (!isset($type['type']) || !isset($attribute['attribute']) || !isset($type['attributes']['defined'][$attribute['attribute']])) {
423 drupal_not_found();
424 }
425
426 drupal_set_title(t('Edit @attribute Attribute', array('@attribute' => $attribute['name'])));
427 rpg_include_admin();
428 return _rpg_admin_attribute_edit_form($type, $type['attributes']['defined'][$attribute['attribute']]);
429 }
430
431 function rpg_admin_type_action_edit($type, $action) {
432 $type = rpg_types($type);
433 $action = rpg_actions($action);
434 if (!isset($type['type']) || !isset($action['action']) || !isset($type['actions']['defined'][$action['action']])) {
435 drupal_not_found();
436 }
437
438 drupal_set_title(t('Edit @action action', array('@action' => $action['name'])));
439 rpg_include_admin();
440 return _rpg_admin_action_edit_form($type, $type['actions']['defined'][$action['action']]);
441 }
442
443 function rpg_admin_type_event_edit($type, $event) {
444 $type = rpg_types($type);
445 $event = rpg_events($event);
446 if (!isset($type['type']) || !isset($event['event']) || !isset($type['events']['defined'][$event['event']])) {
447 drupal_not_found();
448 }
449
450 drupal_set_title(t('Edit @event event', array('@event' => $event['name'])));
451 rpg_include_admin();
452 return _rpg_admin_event_edit_form($type, $type['events']['defined'][$event['event']]);
453 }
454
455 function rpg_admin_type_attribute_add_submit($form_id, $form_values) {
456 // include rpg_attribute_add
457 rpg_include_loadcache();
458
459 $attribute = array();
460 $attribute['attribute'] = $form_values['attribute'];
461 $attribute['name'] = $form_values['name'];
462 $attribute['class'] = $form_values['class'];
463 $attribute['get'] = $form_values['get'];
464 $attribute['verify'] = $form_values['verify'];
465 $attribute['set'] = $form_values['set'];
466 $attribute['default_value'] = $form_values['default_value'];
467 $attribute['form_display'] = $form_values['form_display'];
468
469 $attribute = rpg_attribute_add($form_values['type'], $attribute);
470 // $attribute = rpg_attribute_add($form_values['type'], $form_values['class'], $form_values['attribute'], $form_values['name']);
471
472 if (isset($attribute)) {
473 drupal_set_message(t("The attribute '@attribute' has been added.", array('@attribute' => $form_values['name'])));
474 return 'admin/rpg/types/type/' . $form_values['type'] . '/attributes/edit/' . $attribute['attribute'];
475 }
476 }
477
478 function rpg_admin_type_attribute_edit_submit($form_id, $form_values) {
479 // include rpg_attribute_edit
480 rpg_include_loadcache();
481
482 $attribute = array();
483 $attribute['attribute'] = $form_values['attribute'];
484 $attribute['name'] = $form_values['name'];
485 $attribute['class'] = $form_values['class'];
486 $attribute['description'] = $form_values['description'];
487 $attribute['get'] = $form_values['get'];
488 $attribute['verify'] = $form_values['verify'];
489 $attribute['set'] = $form_values['set'];
490 $attribute['default_value'] = $form_values['default_value'];
491 $attribute['form_display'] = $form_values['form_display'];
492
493 $class = rpg_attribute_classes($attribute['class']);
494
495 $settings = module_invoke($class['module'], 'rpg_attribute_class_settings', 'save', $class['class']);
496
497 foreach ((array)($settings) as $key => $setting) {
498 $attribute[$setting] = $form_values[$setting];
499 }
500
501 $attribute = _rpg_attribute_edit($form_values['type'], $attribute);
502
503 drupal_set_message(t('Your changes have been recorded.'));
504 return 'admin/rpg/types/type/' . $form_values['type'] . '/attributes/edit/' . $attribute['attribute'];
505 }
506
507 function rpg_admin_type_action_add_submit($form_id, $form_values) {
508 // include _rpg_action_add
509 rpg_include_loadcache();
510
511 $action = array();
512 $action['action'] = $form_values['action'];
513 $action['name'] = $form_values['name'];
514
515 $action = _rpg_action_add($form_values['type'], $action); //$form_values['action'], $form_values['name']);
516
517 drupal_set_message(t("The action '@action' has been added.", array('@action' => $form_values['name'])));
518 return 'admin/rpg/types/type/' . $form_values['type'] . '/actions/edit/' . $action['action'];
519 }
520
521 /**
522 * This is when you submit the action edit admin screen. makes any changes to that action, which will
523 * be inherited by inherited types.
524 */
525 function rpg_admin_type_action_edit_submit($form_id, $form_values) {
526 // include rpg_action_edit
527 rpg_include_loadcache();
528 $type = rpg_types($form_values['type']);
529
530 $action = array(
531 'action' => $form_values['action'],
532 'name' => $form_values['name'],
533 'type' => $type['type'],
534 'description' => $form_values['description'],
535 'php' => $form_values['php'],
536 'expose' => $form_values['expose'],
537 );
538
539 $action = _rpg_action_edit($type['type'], $action);
540
541 drupal_set_message(t('Your changes have been recorded.'));
542 return 'admin/rpg/types/type/' . $form_values['type'] . '/actions/edit/' . $action['action'];
543 }
544
545 function rpg_admin_type_event_add_submit($form_id, $form_values) {
546 // include _rpg_event_add
547 rpg_include_loadcache();
548
549 $event = array();
550 $event['event'] = $form_values['event'];
551 $event['name'] = $form_values['name'];
552
553 $event = _rpg_event_add($form_values['type'], $event); //$form_values['event'], $form_values['name']);
554
555 drupal_set_message(t("The event '@event' has been added.", array('@event' => $form_values['name'])));
556 return 'admin/rpg/types/type/' . $form_values['type'] . '/events/edit/' . $event['event'];
557 }
558
559 /**
560 * This is when you submit the event edit admin screen. makes any changes to that event, which will
561 * be inherited by inherited types.
562 */
563 function rpg_admin_type_event_edit_submit($form_id, $form_values) {
564 // include rpg_event_edit
565 rpg_include_loadcache();
566 $type = rpg_types($form_values['type']);
567
568 $event = array(
569 'event' => $form_values['event'],
570 'name' => $form_values['name'],
571 'type' => $type['type'],
572 'description' => $form_values['description'],
573 'php' => $form_values['php'],
574 'expose' => $form_values['expose'],
575 );
576
577 $event = _rpg_event_edit($type['type'], $event);
578
579 drupal_set_message(t('Your changes have been recorded.'));
580 return 'admin/rpg/types/type/' . $form_values['type'] . '/events/edit/' . $event['event'];
581 }
582
583 /**
584 * This loads all defined attributes into the system.
585 * @param $attribute
586 * If given, then return an array defining that attribute (by machine-readable name).
587 * Otherwise, return an array of all attributes.
588 * @param $cached
589 * If false, then force the cache to be reloaded. Otherwise, simply return the attribute(s)
590 * from the cache.
591 */
592 function rpg_attributes($attribute = NULL, $cached = true) {
593 static $attributes;
594 if (!isset($attributes) || !$cached) {
595 if ($cached && $cache = cache_get('rpg_attributes', 'cache_rpg')) {
596 $attributes = unserialize($cache->data);
597 }
598 else {
599 // define _rpg_load_attributes();
600 rpg_include_loadcache();
601 $attributes = _rpg_load_attributes();
602 cache_set('rpg_attributes', 'cache_rpg', serialize($attributes));
603 }
604 }
605 if (isset($attribute)) {
606 return $attributes[$attribute];
607 }
608 return $attributes;
609 }
610
611 /**
612 * This loads all defined actions into the system.
613 * @param $action
614 * If given, then return an array defining that action (by machine-readable name).
615 * Otherwise, return an array of all actions.
616 * @param $cached
617 * If false, then force the cache to be reloaded. Otherwise, simply return the action(s)
618 * from the cache.
619 */
620 function rpg_actions($action = NULL, $cached = true) {
621 static $actions;
622 if (!isset($actions) || !$cached) {
623 if ($cached && $cache = cache_get('rpg_actions', 'cache_rpg')) {
624 $actions = unserialize($cache->data);
625 }
626 else {
627 // define _rpg_load_actions();
628 rpg_include_loadcache();
629 $actions = _rpg_load_actions();
630 cache_set('rpg_actions', 'cache_rpg', serialize($actions));
631 }
632 }
633 if (isset($action)) {
634 return $actions[$action];
635 }
636 return $actions;
637 }
638
639 /**
640 * This loads all defined events into the system.
641 * @param $event
642 * If given, then return an array defining that event (by machine-readable name).
643 * Otherwise, return an array of all events.
644 * @param $cached
645 * If false, then force the cache to be reloaded. Otherwise, simply return the event(s)
646 * from the cache.
647 */
648 function rpg_events($event = NULL, $cached = true) {
649 static $events;
650 if (!isset($events) || !$cached) {
651 if ($cached && $cache = cache_get('rpg_events', 'cache_rpg')) {
652 $events = unserialize($cache->data);
653 }
654 else {
655 // define _rpg_load_events();
656 rpg_include_loadcache();
657 $events = _rpg_load_events();
658 cache_set('rpg_events', 'cache_rpg', serialize($events));
659 }
660 }
661 if (isset($event)) {
662 return $events[$event];
663 }
664 return $events;
665 }
666
667 function rpg_admin_type_actions($type) {
668 $type = rpg_types($type);
669 if (!$type['type']) {
670 drupal_not_found();
671 }
672
673 drupal_set_title(t('Actions for @type', array('@type' => $type['name'])));
674
675 // include the include file w/ our helper function
676 rpg_include_admin();
677
678 return _rpg_admin_type_actions_form($type);
679 }
680
681 function rpg_admin_type_events($type) {
682 $type = rpg_types($type);
683 if (!$type['type']) {
684 drupal_not_found();
685 }
686
687 drupal_set_title(t('Events for @type', array('@type' => $type['name'])));
688
689 // include the include file w/ our helper function
690 rpg_include_admin();
691
692 return _rpg_admin_type_events_form($type);
693 }
694
695 /**
696 * this is the main function called to play each turn, from the /rpg/play menu item
697 */
698 function rpg_play($object = NULL, $action = NULL, $who = NULL, $target = NULL, $item = NULL) {
699 global $rpg, $user;
700 // if we already have a character, process the action. otherwise, show a select screen
701 if ($rpg['pc']) {
702 // allow modules to hijack the process, such as during character creation
703 // in fact, character selection can happen elsewhere as well!
704 module_invoke_all('rpg_play', $rpg['pc']);
705
706 // if we have an action, such as rpg/play/88/drop/53/88, then process it
707 // TODO: move this to rpg_action? and add all args to the module_invoke, thus this function will have no args!
708 if (isset($action)) {
709 $action = array(
710 '#object' => $object,
711 '#action' => $action,
712 '#who' => $who ? $who : $rpg['pc'],
713 '#target' => $target,
714 '#what' => $item,
715 );
716 rpg_action_process($action);
717 // make sure the pc is able to act (next action round, etc)
718 /* if (rpg_may_act($pc)) {
719 // create the event from the action, saving it to check later
720 $event = rpg_create_event($pc, $action, $target, $what);
721
722 // process our new event
723 rpg_event_process_threads(1);
724
725 // make sure it's been processed. if not, we'll set a message to that effect
726 rpg_event_check_if_processed($event);
727 }
728 else {
729 // create a message saying the pc isn't able to act yet
730 // TODO: seperate from rpg_message.module. maybe make it simply a drupal_set_message? or better yet, a theme function
731 $message = array(
732 '#who' => $pc,
733 '#message' => t('You may not yet act.'),
734 );
735 rpg_message($message);
736 }*/
737 }
738 // display our game play page
739 return theme('rpg_play', $rpg['pc']);
740 }
741 else {
742 // we don't have a character yet, so go to the character selection page
743 drupal_goto('rpg/select');
744 }
745 }
746
747 function rpg_character_select_page() {
748 global $user;
749
750 // clear any old characters (effective 'logging off' the character)
751 $sql = "UPDATE {rpg_pc} SET active=0 WHERE uid=%d";
752 db_query($sql, $user->uid);
753
754 $pcs = rpg_user_pcs($user);
755 $selected = arg(2);
756
757 // if we have selected a pc, then set the active pc for that user to it and go to rpg/play
758 if ($selected && in_array($selected, $pcs)) {
759 $sql = "UPDATE {rpg_pc} SET active=1 WHERE uid=%d AND rid=%d";
760 db_query($sql, $user->uid, $selected);
761 drupal_goto('rpg/play');
762 }
763
764 // print our list of available characters for this user
765 $output .= theme('rpg_character_select_page', $user, $pcs);
766 return $output;
767 }
768
769 /**
770 * this will return an array of the pc object id's owned by a certain user $account
771 */
772 function rpg_user_pcs($account) {
773 static $pcs = array();
774 if (!isset($pcs[$account->uid])) {
775 $pcs[$account->uid] = array();
776 $sql = "SELECT rid FROM {rpg_pc} WHERE uid=%d";
777 $results = db_query($sql, $account->uid);
778 while ($result = db_fetch_array($results)) {
779 $pcs[$account->uid][] = $result['rid'];
780 }
781 }
782 return $pcs[$account->uid];
783 }
784
785 /**
786 * callback for rpg/create/[TYPE]
787 *
788 * this either displays a list of types, or the form to create an object of the specified type.
789 */
790 function rpg_create_page() {
791 $type = arg(3);
792 if (arg(2) == 'pc') {
793 return rpg_create_pc_page();
794 }
795 if (!$type) {
796 global $user;
797 $types = rpg_types();
798 $pc_types = rpg_available_pc_types($user);
799 $admin_access = user_access('rpg admin');
800 if (empty($types) && $admin_access) {
801 $output .= t('There are currently no RPG object types defined. You may define these at the !admin.', array('!admin' => l(t('types admin page'), 'admin/rpg/types')));
802 }
803 foreach ($types as $type) {
804 $link = '';
805 $is_pc_type = isset($pc_types[$type['type']]);
806 // TODO: this needs to be redone; actor is now in the realm of rulesets
807 if ($is_pc_type && ($admin_access || (rpg_user_access('create pc', $type) && rpg_may_create_pc($user, $type)))) {
808 $link = l($type['name'], 'rpg/create/pc/' . $type['type']);
809 }
810 else if (!$is_pc_type && ($admin_access || rpg_user_access('create', $type))) {
811 $link = l($type['name'], 'rpg/create/type/' . $type['type']);
812 }
813 if ($link) {
814 $output .= '<p>' . $link . '<br />' . $type['shortdesc'] . '</p>';
815 }
816 }
817
818 if ($output) {
819 // TODO: theme this
820 return $output;
821 }
822 else {
823 drupal_access_denied();
824 }
825 }
826 else {
827 return rpg_create_object_page($type, arg(4));
828 }
829 }
830
831 function rpg_edit_page() {
832 if (arg(2) && is_numeric(arg(2))) {
833 $object = rpg_object(arg(2));
834 if ($object->rid) {
835 if (!rpg_edit_access($object)) {
836 drupal_access_denied();
837 }
838 $form = drupal_get_form('rpg_form_object', $object);
839 return theme('rpg_edit_object_page', $object, $form);
840 }
841 }
842 drupal_not_found();
843 }
844
845 function rpg_view_page() {
846 if (is_numeric(arg(2))) {
847 $object = arg(2);
848 global $rpg;
849 if (!rpg_view_access($rpg['pc'], $object)) {
850 drupal_access_denied();
851 }
852 if (!rpg_id($object)) {
853 drupal_not_found();
854 }
855 return theme('rpg_view_object_page', $object);
856 }
857 else {
858 if (!user_access('administer rpg')) {
859 drupal_access_denied();
860 }
861 $sql = "SELECT rid FROM {rpg} ORDER BY created DESC";
862 $limit = variable_get('rpg_browse_object_limit', 20);
863 $results = pager_query($sql, $limit);
864 $objects = array();
865 while ($rid = db_fetch_array($results)) {
866 $objects[$rid['rid']] = $rid['rid'];
867 }
868 return theme('rpg_browse_page', $objects);
869 }
870 drupal_not_found();
871 }
872
873 /**
874 * this displays the form to create an object of $type
875 *
876 * @param $type
877 * This is the string of the object.
878 * @param $subtype
879 * This will set the object type as $subtype, assuming $subtype is a child of $type.
880 */
881 function rpg_create_object_page($type, $subtype = NULL) {
882 global $user;
883 $type = rpg_types($type);
884 if ($subtype) {
885 $subtype = rpg_types($subtype);
886 if (!rpg_type_is_a($subtype, $type['type'])) {
887 drupal_not_found();
888 }
889 else {
890 $type = $subtype;
891 }
892 }
893 if (!user_access('admin rpg') && !rpg_user_access('create', $type)) {
894 drupal_access_denied();
895 }
896 $object = rpg_new_rpg_object($type, $user);
897 $form = drupal_get_form('rpg_form_object', $object);
898 return theme('rpg_create_object_page', $object, $type, $form);
899 }
900
901 function rpg_available_pc_types($user) {
902 static $avail;
903
904 if (!isset($avail)) {
905 $avail = array();
906 $pc_types = module_invoke_all('rpg_pc_types', $user);
907 $types = rpg_types();
908 foreach ($pc_types as $type) {
909 if (isset($types[$type])) {
910 $avail[$type] = $types[$type];
911 }
912 }
913 }
914 return $avail;
915 }
916
917 function rpg_create_pc_page() {
918 global $user;
919 $pc_types = rpg_available_pc_types($user);
920 $pc_type = arg(3);
921 if ($pc_type && isset($pc_types[$pc_type])) {
922 $type = $pc_types[$pc_type];
923 }
924 else if ($pc_type) {
925 drupal_not_found();
926 }
927 if (isset($type['type'])) {
928 // we call this, because an admin might set certain user roles to have only a limited number of pc's
929 // or maybe, for instance, only one of each race or class
930 if (user_access('admin rpg') || (rpg_user_access('create pc', $type) && rpg_may_create_pc($user, $type))) {
931 $pc = rpg_new_rpg_object($type, $user);
932 $form = drupal_get_form('rpg_form_object', $pc);
933 return theme('rpg_create_pc_page', $pc, $type, $form);
934 }
935 else {
936 drupal_access_denied();
937 }
938 }
939 $available_types = array();
940 foreach($pc_types as $type) {
941 // we call this, because an admin might set certain user roles to have only a limited number of pc's
942 // or maybe, for instance, only one of each race or class
943 if (rpg_may_create_pc($user, $type)) {
944 $available_types[] = l($type['name'], 'rpg/create/pc/' . $type['type']) . '<br />' . $type['shortdesc'];
945 }
946 }
947 $output .= theme('item_list', $available_types);
948 return $output;
949 }
950
951 function rpg_form_object($object) {
952 // make sure we have all data loaded
953 if (!$object->is_new) {
954 $object = rpg_object($object, FALSE, TRUE);
955 }
956
957 $form = array();
958
959 // TODO: make this a username lookup instead
960 $form['uid'] = array(
961 '#type' => 'value',
962 '#value' => $object->uid,
963 );
964 $form['types'] = array(
965 '#type' => 'value',
966 '#value' => $object->types,
967 );
968 if (user_access('administer rpg')) {
969 $options = array();
970 foreach (rpg_types() as $type) {
971 $options[$type['type']] = $type['name'];
972 }
973 $form['types'] = array(
974 '#type' => 'select',
975 '#title' => t('Types'),
976 '#options' => $options,
977 '#default_value' => $object->types,
978 '#description' => t('Select the type(s) for this object. Note that conflicts may arise when using multiple types.'),
979 '#multiple' => TRUE,
980 );
981 }
982 $form['created'] = array(
983 '#type' => 'value',
984 '#value' => $object->created,
985 );
986 $form['is_new'] = array(
987 '#type' => 'value',
988 '#value' => $object->is_new,
989 );
990 $form['rid'] = array(
991 '#type' => 'value',
992 '#value' => $object->rid,
993 );
994 $form['data'] = array(
995 '#tree' => true,
996 );
997
998 if (is_array($object->data)) {
999 foreach ($object->data as $attr => $value) {
1000 $attribute = rpg_object_attributes($object->rid, $attr);
1001 $attribute['value'] = $value;
1002 $attribute['object'] = $object;
1003 $class = rpg_attribute_classes($attribute['class']);
1004 $form['data'][$attr] = module_invoke($class['module'], 'rpg_attribute_class_settings', 'form', $class['class'], $attribute);
1005 if (is_null($form['data'][$attr])) {
1006 // display our default form data
1007 $form['data'][$attr] = array(
1008 '#type' => 'textfield',
1009 '#title' => $attribute['name'],
1010 '#default_value' => $attribute['value'],
1011 '#description' => $attribute['description'],
1012 );
1013 }
1014 $display = _rpg_call_function('form_display', $attr, $object->rid);
1015 if ($display === FALSE) {
1016 $form['data'][$attr]['#type'] = 'value';
1017 $form['data'][$attr]['#value'] = $form['data'][$attr]['#default_value'];
1018 }
1019 }
1020 }
1021
1022 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 45);
1023
1024 return $form;
1025 }
1026
1027 // TODO: make sure inserts work ok
1028 function rpg_form_object_submit($form_id, $form_values) {
1029 if ($form_values['is_new']) {
1030 $object = rpg_create($form_values['types'], (array)$form_values['data']);
1031 }
1032 else {
1033 $object = array(
1034 'types' => $form_values['types'],
1035 'uid' => $form_values['uid'],
1036 'created' => ($form_values['created'] ? $form_values['created'] : time()),
1037 'changed' => time(),
1038 'rid' => $form_values['rid'],
1039 'data' => array(),
1040 );
1041
1042 $object = (object) $object;
1043 rpg_save_rpg($object);//print_r($form_values);//exit();
1044 foreach(rpg_object_attributes($object) as $attribute) {//print_r($attribute);
1045 if (isset($form_values['data'][$attribute['attribute']])) {drupal_set_message('attr: ' . $attribute['attribute'].', '.$form_values['data'][$attribute['attribute']]);
1046 rpg_set($attribute['attribute'], $object, $form_values['data'][$attribute['attribute']]);
1047 }
1048 else if ($attribute['default_value']) {drupal_set_message('attr: '.$attribute['attribute'].', default: '.$attribute['default']);
1049 rpg_set($attribute['attribute'], $object, $attribute['default_value']);
1050 }
1051 }
1052 // foreach ((array)$form_values['data'] as $attr => $value) {
1053 // rpg_set($attr, $object, $value);
1054 // }
1055 }
1056
1057 _rpg_save_all_marked_objects();
1058 if (arg(2) == 'pc') {
1059 return 'rpg/select/' . $object->rid;
1060 }
1061 return 'rpg/view/' . $object->rid;
1062 }
1063
1064 /**
1065 * creates a new rpg, saves it in the db, with assigned attribute values. then triggers on_create event and returns new object id
1066 */
1067 function rpg_create($types = array(), $attributes = array()) {
1068 if (is_string($types)) {
1069 $types = (array) $types;
1070 }
1071 if (is_array($types) && !empty($types)) {
1072 $object = array(
1073 'types' => $types,
1074 'uid' => $user->uid,
1075 'created' => time(),
1076 'changed' => time(),
1077 'data' => (array) $attributes,
1078 'is_new' => TRUE,
1079 );
1080 $object = _rpg_insert_new_rpg((object)$object);
1081 rpg_trigger('on_create', $object);
1082 return $object;
1083 }
1084 }
1085
1086 /**
1087 * clones an object, making an identical copy. returns the new object, or NULL if failed
1088 */
1089 function rpg_clone($object) {
1090 // load the full object to clone
1091 $object = rpg_object($object, FALSE, TRUE);
1092 if ($object->rid) {
1093 return rpg_create($object->types, $object->data);
1094 }
1095 }
1096
1097 /**
1098 * returns true if the user has specific $op access to the $type
1099 * @param $op
1100 * 'create' => user may create objects of that type
1101 * 'edit' => user may edit any object of the type
1102 * 'edit own' => user may edit any object of the type they own
1103 * @param $type
1104 * the string or type array of the object type to create/edit
1105 * @return
1106 * true if allowed; false if not allowed
1107 */
1108 function rpg_user_access($op, $type) {
1109 $return = user_access($op . ' rpg object') || user_access('admin rpg');
1110 // TODO: add hooks for specific types
1111 return $return;
1112 }
1113
1114 /**
1115 * is a user allowed to create a pc of the specific type?
1116 * we call this, because an admin might set certain user roles to have only a limited number of pc's
1117 * or maybe, for instance, only one of each race or class
1118 * @param $account
1119 * the user account to check
1120 * @param $type
1121 * the rpg type array to test
1122 * @return
1123 * true if the user may create a new pc of the type, false if not.
1124 */
1125 function rpg_may_create_pc($account, $type) {
1126 if (!user_access('create pc rpg object')) {
1127 return false;
1128 }
1129 // TODO: call hooks to refine access
1130 return true;
1131 }
1132
1133 /**
1134 * return a list of all available rpg types
1135 * @param $type
1136 * if given, just return the type specified by the string
1137 * otherwise, return an array of all types
1138 * @param $cached
1139 * if true, return the cached value, if it's cached.
1140 * otherwise, reload all the type data from include files
1141 */
1142 function rpg_types($type = NULL, $cached = true) {
1143 static $types;
1144
1145 // if it's a cachable request, try to load a cached value
1146 if (!isset($types) || !$cached) {
1147 if ($cached && $cache = cache_get('rpg_types', 'cache_rpg')) {
1148 $types = unserialize($cache->data);
1149 }
1150 else {
1151 // _rpg_type_load_types is in an external include
1152 rpg_include_loadcache();
1153
1154 $types = _rpg_type_load_types();
1155 cache_set('rpg_types', 'cache_rpg', serialize($types));
1156 }
1157 }
1158
1159 // just return the type we're looking for
1160 if (is_string($type)) {
1161 return $types[$type];
1162 }
1163 else if (isset($type)) {
1164 return;
1165 }
1166
1167 return $types;
1168 }
1169
1170 /**
1171 * this will load all module-defined attribute classes, such as node, sprite, map, integer, string, and figured
1172 * TODO: cache this!
1173 *
1174 * @param $class
1175 * this will return just the specific class if specified. otherwise, it will return an array with all the classes.
1176 * @param $refresh
1177 * if set to true, it will force the list to be refreshed.
1178 */
1179 function rpg_attribute_classes($class = NULL, $refresh = FALSE) {
1180 static $classes;
1181 if (!isset($classes) || $refresh) {
1182 $classes = array();
1183 foreach (module_implements('rpg_attribute_class_settings') as $module) {
1184 $attributes = module_invoke($module, 'rpg_attribute_class_settings', 'info');
1185 foreach ($attributes as $attribute_class => $info) {
1186 $info['class'] = $attribute_class;
1187 $info['module'] = $module;
1188 rpg_drupal_alter('rpg_class', $info);
1189 $classes[$attribute_class] = $info;
1190 }
1191 }
1192 }
1193
1194 // just return the class we're looking for
1195 if ($class) {
1196 return $classes[$class];
1197 }
1198 return $classes;
1199 }
1200
1201 /**
1202 * load functions that rebuild cached arrays, such as rpg_types and attributes
1203 */
1204 function rpg_include_loadcache() {
1205 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.loadcache.inc');
1206 }
1207
1208 /**
1209 * load functions needed for admin pages
1210 */
1211 function rpg_include_admin() {
1212 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.admin.inc');
1213 }
1214
1215 /**
1216 * load functions needed for rpg object database, such as getting & setting object attributes to/from the db
1217 */
1218 function rpg_include_db() {
1219 require_once(drupal_get_path('module', 'rpg') . '/inc/rpg.db.inc');
1220 }
1221
1222 function rpg_is_a($object, $type) {
1223 $object = rpg_object($object);
1224 foreach ((array)$object->types as $ancestor) {
1225 if (rpg_type_is_a($ancestor, $type)) {
1226 return true;
1227 }
1228 }
1229 return false;
1230 }
1231
1232 /**
1233 * recursive function to check to see if a type has a specific ancestor
1234 * @param $type
1235 * string containing the type to check OR the array of a type returned by rpg_types($type)
1236 * @param $ancestor
1237 * string containing the type to see if we're an ancestor of
1238 */
1239 function rpg_type_is_a($type, $ancestor) {
1240 if (is_string($type)) {
1241 $type = rpg_types($type);
1242 }
1243 if ($type['type'] == $ancestor) {
1244 return true;
1245 }
1246 foreach ((array)$type['parents'] as $parent) {
1247 if ($parent == $ancestor) {
1248 return true;
1249 }
1250