The missing judge

§
by
Jesper bisgaard

One day when the players are sitting at their favorite barrens soycaf place a full scale gang war breakout right in the street where they are sitting. If one of the runners has a gang contact he or she can tell the runner that someone has kidnapped the judge, if no runner has such a contact perceptions checks will reveal that the gangs are accusing each other of the crime.

The judge is a powerful mage who acts as a broker between the gangs to avoid conflicts that could escalate to a war.

He has in fact been kidnapped by an agricultural firm by the name of Sivak which is in league with Wuxing and they are looking to expand into several areas of the barrens inhabited by the gangs.

Search for the elvish song stones

§
by
Jesper bisgaard

A dwarf in an expensive suade suite hires the runners to find five elvish song stones. A song stone is a jewel imbued with magical properties to play music and display wonderful visual colors at the same time. They each go for around 10.000 on the black marked. He offers the runners 3000¥ for each stone they get.

There are sixteem stones in Seattle and they are divided between 6 people.

  1. An elven black marked dealer has 2 stones.
  2. A human stock broker has 3 stone.
  3. A famous human singer has1 stones.
  4. The space needle has 1 on display.
  5. A local business owner has 2 stones (Gregory Jones).
  6. The seattle museum of arts has 7 stones on display, borrowed to them by the Tir Tairngire.

Hostile takeover

§
by
Jesper bisgaard

A company man hires the runners to steal a local companys research data. The company "NABER - New age bio enginering" resides in Snohomish and does a lot if research into biology and agriculture. Mr Johnson is from Ares and has orders to pave the way for a purchase of NABER. He offers the runners 5000 and 10% of the research's value.

NABER has employed a team of shadowrunners of their own to protect their recent findings, there is a decker, a rigger, a mage and 2 steet samurais.

The main shareholder in the company is Howard Jackson, there are 8 bordmembers and they have contracted a private company for aditional security "Firelance security".

Shapeshifter hunt

§
by
Jesper bisgaard

The runners are hired to kill a shapeshifter who recently arrived in town. Mr Johnson wants the shifters corps returned.

The shapeshifter is hiding out in a park in Bellevou and has two shaman friends protecting him and his presence in astral space and there are 12 ghouls living in the sewers around the park who also aid the shifter. The shapeshifter is a huge brown hairs man with a scar across the right eye and is well connected in the magic community. He has come to Seattle to find another shapeshifter "Mave" who has been missing for some time. Mave has been captured by Mitsuhama and is being held hostage in an isolation cell in the basement of a hidden lab in an old apartment building. Mitsuhama is doing research into shapeshifter anatomy and how to use it for military purposes and they are using Mave as a lab rat. The laboratory is being guarded by 5 drones controlled by a rigger and 6 guards. There are 10 scientists in the lab.

Openlayers behavior

§
by
Jesper bisgaard

I recently worked on a site where they had a map of their stores which enabled users to search for their local store. Each store had a popup if you clicked on the icon with the store name and a link to the store page, where you could find opening hours and additional information about the perticular store.

The customer wanted to change this so clicking on the store icon would send the user directly to the store page instead of opening a popup. The map was implemented using openlayers and there wasn't an exsiting behavior for this, which gave me the oppotunity to try and make a new open layers behavior.

First of all you need to create a module or modify an existing and you would properly want to add a dependency on openlayers.

Next you need to add hook_openlayers_behaviors()

function my_module_openlayers_behaviors() {
  return array(
    'openlayers_behavior_my_behavior' => array(
      'title' => t('My Behavior'),
      'description' => t('On click go to store page'),
      'type' => 'layer',
      'path' => drupal_get_path('module', 'my_module') . '/includes/behaviors',
      'file' => 'openlayers_behavior_my_behavior.inc',
      'behavior' => array(
        'class' => 'openlayers_behavior_my_behavior',
        'parent' => 'openlayers_behavior',
      ),
    ),
  );
}

This will notify openlayers that you have a new behavior in your includes/behaviors folder.

In the openlayers_behavior_my_behavior.inc file you then declase a php class which describes how your behavior will work. In this class you have a number of options to acts on different stages of your behaviors life, but for this task we don't need to do much.

class openlayers_behavior_mybehavior extends openlayers_behavior {}
  function options_init() {
    return array();
  }

  function options_form($defaults) {
    return array();
  }

  function render(&$map) {
    drupal_add_js(drupal_get_path('module', 'mybehavior') . '/includes/behaviors/js/openlayers_behavior_my_behavior.js');
    return $this->options;
  }
}

The options_init function provides initial values for the options_form function. The options_form allows you to provide a number of settings for your behavior. Many behaviors will need this function but it is not required.

The render function is where the magic happens and it is a required function for the behavior. Here you can return any custom markup for your behavior but our case only needs to provide a javascript file.

If you would like to see some examples of the behavior class, checkout the default behaviors provided with the openlayers module. Especially the popup behavior I found to be a good starting point.

Next we have our javascript.

Drupal.theme.prototype.openlayersLinkToNode = function(feature) {
  var output = '';
  return output;
};

// Make sure the namespace exists
Drupal.openlayers.linkToNode = Drupal.openlayers.linkToNode || {};

/**
 * OpenLayers go to node Behavior
 */
Drupal.openlayers.addBehavior('openlayers_behavior_link_to_node', function (data, options) {
  var map = data.openlayers;
  var layers = [];
  var selectedFeature;

  // For backwards compatiability, if layers is not
  // defined, then include all vector layers
  if (typeof options.layers == 'undefined' || options.layers.length == 0) {
    layers = map.getLayersByClass('OpenLayers.Layer.Vector');
  }
  else {
    for (var i in options.layers) {
      var selectedLayer = map.getLayersBy('drupalID', options.layers[i]);
      if (typeof selectedLayer[0] != 'undefined') {
        layers.push(selectedLayer[0]);
      }
    }
  }

  // if only 1 layer exists, do not add as an array.  Kind of a
  // hack, see https://drupal.org/node/1393460
  if (layers.length == 1) {
    layers = layers[0];
  }

  var linkToNodeSelect = new OpenLayers.Control.SelectFeature(layers, {
      onSelect: function(feature) {
        var nid = feature.attributes.nid;
        window.location.href = 'node/' + nid;
      },
    }
  );

  map.addControl(linkToNodeSelect);
  linkToNodeSelect.activate();
  Drupal.openlayers.linkToNode.linkToNodeSelect = linkToNodeSelect;
});

Most of this javascript is there to ensure that the functionality works.

The important part of this script is the OpenLayers.Control.SelectFeature(layers, function) where we add our select functionality

There we go, I hope it helps you on your way to making the openlayers behaviors of your dreams. :)

Wysiwyg button with form #3

§
by
Jesper bisgaard

Welcome to the second article in my tutorial about building a wysiwyg button for drupal 7 which enables use to inserting tokens.

This will be a four step tutorial

  1. Step one will be building the module and a simple wysiwyg button
  2. Step two will cover a simple wysiwyg button
  3. Step three will expand the button with a multi field form
  4. Step four will make this form dynamic with ajax callbacks for removing and adding fields

This next part will cover how to expand the javascript wysiwyg button to include a form with multiple textfields which will the generate the token for displaying our entity.

First we need a form which will allow the user to enter the id of the entity to display using the token. For this we will make a drupal form in our .module file.

function ting_token_insert_form($form, &$form_state) {
  drupal_add_library('system', 'ui.dialog');
  $form['#tree'] = TRUE;
 
  $form['objects'] = array(
    '#type' => 'fieldset',
    '#title' => t('Entities'),
    '#prefix' => '<div id="ting-token-fieldset-wrapper">',
    '#suffix' => '</div>',
  );
 
  $form['objects']['entity1'] = array(
    '#type' => 'textfield',
    '#title' => t('Entity id'),
    '#default_value' => '',
  );
  $form['objects']['entity2'] = array(
    '#type' => 'textfield',
    '#title' => t('Entity id'),
    '#default_value' => '',
  );
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
    '#attributes' => array(
      'class' => array('form-save-ids'),
    ),
  );

  return $form;
}

Now with the form ready we need to create a way for our javascript to retrieve it, and for this we need a menu hook and a custom content function.

/**
 * implements hook_menu
 */
function example_menu() {
  $items = array();
  $items['example/insert/nojs'] = array(
    'page callback' => 'example_get_insert_form',
    'page arguments' => array(2),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['example/insert/ajax'] = array(
    'delivery callback' => 'ajax_deliver'
  ) + $items['example/insert/nojs'];
  return $items;
}

function example_get_insert_form($ajax) {
  $is_ajax = $ajax === 'ajax';
  $form = drupal_get_form('example_insert_form', $ids);
  if ($is_ajax) {
    $form = drupal_render($form);
    die($form);
  }
  else {
    return $form;
  }
}

The menu hook is pretty strait forward, it uses drupals ajax system to handle the data return. The example_get_insert_form returns a rendered form is the parameter is ajax and a form render array if it's not. Now we are ready to rewrite our javascript.

// $Id$
(function ($) {
 
Drupal.wysiwyg.plugins['tokenInsert'] = {
 
  /**
   * Return whether the passed node belongs to this plugin (note that "node" in this context is a JQuery node, not a Drupal node).
   *
   * We identify code managed by this example plugin by giving it the HTML class
   * 'tokenInsert'.
   */
  isNode: function(node) {
    res = $(node).is('.tokenInsert');
    return ($(node).is('.tokenInsert'));
  },
 
  /**
   * Invoke is called when the toolbar button is clicked.
   */
  invoke: function(data, settings, instanceId) {
    // Typically, an icon might be added to the WYSIWYG, which HTML gets added
    // to the plain-text version.
    if (data.format == 'html') {
      var content = this._getIds(data.content);
      if(content !== '') {
        settings.ids = content
      } else {
        settings.ids = '';
      }
    }
    else {
      var content = '<!--exampleInsert-->';
    }
    if (typeof content !== 'undefined') {
      Drupal.wysiwyg.plugins.tokenInsert.insert_form(data, settings, instanceId);
    }
  },
  insert_form: function (data, settings, instanceId) {
    // Location, where to fetch the dialog.
    var aurl = Drupal.settings.basePath + 'example/insert/ajax';
    var dialogdiv = $('<div id="example-insert-dialog"></div>');
    dialogdiv.load(aurl, function(){
      var dialogClose = function () {
        try {
          dialogdiv.dialog('destroy').remove();
        } catch (e) {};
      };
      var btns = {};
      btns[Drupal.t('Cancel')] = function () {
        $(this).dialog("close");
      };
      var $this = this;
      dialogdiv.find('.form-save-ids').click(function(evt) {
        evt.preventDefault();
        var ids = [],
          $items = dialogdiv.find('#ting-token-fieldset-wrapper .form-text');
        $items.each(function() {
          ids.push($(this).val());
        });
        settings.tingIds = ids;
        var content = Drupal.wysiwyg.plugins['tokenInsert']._getPlaceholder(settings);
        Drupal.wysiwyg.instances[instanceId].insert(content);
        dialogdiv.dialog("close");
      });
      dialogdiv.dialog({
        modal: true,
        autoOpen: false,
        closeOnEscape: true,
        resizable: true,
        draggable: true,
        autoresize: true,
        namespace: 'jquery_ui_dialog_default_ns',
        dialogClass: 'jquery_ui_dialog-dialog',
        title: Drupal.t('Insert'),
        buttons: btns,
        width: '70%',
        close: dialogClose
      });
      dialogdiv.dialog("open");
      Drupal.attachBehaviors();
    });
  },
  /**
   * Replace all <!--exampleInsert--> tags with the icon.
   */
  attach: function(content, settings, instanceId) {
    content = content.replace(/<!--exampleInsert-->/g, this._getPlaceholder(settings));
    return content;
  },
 
  /**
   * Replace the icons with <!--exampleInsert--> tags in content upon detaching editor.
   */
  detach: function(content, settings, instanceId) {
    var $content = $('<div>' + content + '</div>');
    $.each($('.exampleInsert', $content), function (i, elem) {
      elem.parentNode.removeChild(elem);
    });
    return $content.html();
  },
 
  /**
   * Helper function to return a HTML placeholder.
   */
  _getPlaceholder: function (settings) {
    if(settings.ids) {
      return '[ting:teaser:' + settings.ids.join() + ']';
    }
    return '';
  },
 
  /**
   * Helper function to return ids from a placeholder.
   */
  _getIds: function (content) {
    var ids = ''
    if(content.indexOf('[ting:teaser:') === 0 && content.indexOf(']') === (content.length - 1)) {
      content = content.replace('[ting:teaser:', '');
      content = content.replace(']', '');
      ids = content.split(',');
    }
    return ids;
  }
};
 
})(jQuery);

We can now insert two entities using our new insert form, but we still want more. We want to be able to insert multiple ids using ajax to add and remove id's, so stay tuned for the next tutorial.

16 Dec 2013

Wysiwyg button with form

§
by
Jesper bisgaard

At my work i recently had the opportunity to build a wysiwyg plugin in drupal for formatting a token we had created for a customer. This precented a number of challenges which I though others might also have, so I will try to share them here.

This will be a four step tutorial for drupal 7

  1. Step one will be building the module and a simple wysiwyg button
  2. Step two will cover a simple wysiwyg button
  3. Step three will expand the button with a multi field form
  4. Step four will make this form dynamic with ajax callbacks for removing and adding fields

Step 1 - building the module

First step is to create our module so as always we need an info file:

name = Ting token
description = Provides tokens for inserting ting objects in text fields.
core = 7.x
package = Ding!
files[] = ting_token.module
files[] = ting_token.token.inc

I will presume that you are familier with the setup of an info file so I won't go into details here. Now I will go into the token file for this module.

For these tokens we wanted a format which would insert a view_mode and one or more entities to be rendered. First we need a token file with the file name module_name.tokens.inc, in here we need to define hook_token_info

function example_token_info() {
  $type = array(
    'name' => t('Example entity'),
    'description' => t('Display an entity.'),
  );
 
  // Core tokens for nodes.
  $consent['example'] = array(
    'name' => t("Example entity"),
    'description' => t("Show an entity."),
  );

  return array(
    'types' => array('example' => $type),
    'tokens' => array('example' => $consent),
  );
}

Next up we need to handle the tokens once inserted by defining hook_tokens

function example_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $url_options = array('absolute' => TRUE);
  if (isset($options['language'])) {
    $url_options['language'] = $options['language'];
    $language_code = $options['language']->language;
  }
  else {
    $language_code = NULL;
  }
  $sanitize = !empty($options['sanitize']);
  $replacements = array();
  if ($type == 'example') {
    foreach ($tokens as $name => $original) {
      $args = explode(':', $name);
      $view_mode = array_shift($args);
      $eid = implode(':', $args);
      if(strpos($eid, ',') !== FALSE) {
        $eids = explode(',', rawurldecode($eid));
      } else {
        $eids = array(rawurldecode($eid));
      }
      
      $entities = entity_load('example', $eids);
      $output = '<div class="example-inline-list">';
      foreach($entities as $id => $entity) {
        $object = entity_view($entity, $view_mode);
        if($view_mode == 'list_item') {
          $object['#attributes']['class'][] = format_string('compact');
        }
        $output .= drupal_render($object);
      }
      $output .= '</div>';
      $replacements[$original] = $output;
    }
  }

  return $replacements;
}

For this customer our entities are ting entities so we are using specialized functions for these, but this code should work for most purposes. You might want to specialize the rendering of entities to fit your needs.

For this part of the module we actually don't need to write anything in our module file, but we will need it for the next part.

Drupal 7 development

§
by
Jesper bisgaard

Just a few modules I use when developing:

Devel

Great for debugging while developing.

Search krumo

Once you have outputted your devel data Search krumo enables you to search the data structure and to copy the path to your desired data directly.

Module filter

If your sites have lots of modules Module filter makes your life alot easier.

If you have any more suggestions leave me a comment.

Writing a custom panel context

§
by
Jesper bisgaard

I recently had to create a list of items based on data entered in a field and since the list would live in a panel I decided to create a new context to deliver the id to my views contextual filter.

I was guided by Yuriy Gerasimov on how to write the context and I changed a few things in order to make my case work.

Writing the context i very easy and requires only a very small module, so here is how I went about it:

First we need to create a new module, we could call it custom_context_example. I presume you know how to create a custom module otherwise lookup how on drupal.org.

Well in the custom_context_example.module we need to tell ctools that we are providing a new context type. We do this by defining the hook_ctools_plugin_directory and specifying the local path to our context folder.

/**
  * Implements hook_ctools_plugin_directory().
  */
function custom_context_example_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && $plugin == 'contexts') {
    return "plugins/contexts";
  }
}

Next we create the plugins and contexts folder. In the contexts folder we can now create the context inc file. Lets call it custom_context_example.inc

The first thing we need to do here is define the plugin for ctools:

/**
 * Array to describe plugin.
 */
$plugin = array(
  'title' => t('Custom context example'),
  'description' => t('Provide token for referenced item.'),
  'context' => 'ctools_plugin_create_custom_context_example',
  'context name' => 'custom_context_example',
  'keyword' => 'custom_context_example', // Provides a list of items which are exposed as keywords.
  'convert list' => 'custom_context_example_convert_list', // Convert keywords into data.
  'convert' => 'custom_context_example_convert',
);

Next we need to write the create context function as well as the convert list and the actual convertion.

The create context functiion ctools_plugin_create_context_example starts with defining the base context.

function ctools_plugin_create_custom_context_example($empty, $data = NULL, $conf = FALSE) {
  $context = new ctools_context('custom_context_example');
  $context->plugin = 'custom_context_example';
  $context->data = new stdClass();

next we need to get the object which contains the reference for this context, in this case I knew that the panel would be part of a term view but depending your case you would have to change the next part to match.

  $term = menu_get_object('taxonomy_term', 2)
 
  if(!empty($term) && !empty($term->field_custom_context_example_data)) {
    $field_lang = field_language('taxonomy_term', $term, 'field_custom_context_example_data');
    $data= $term->field_custom_context_example_data[$field_lang][0]['value'];
    $query = db_select('aggregator_category', 'a')
      ->condition('a.title', $category, '=')
      ->fields('a', array('cid', 'title', 'description'));
    $result = $query->execute();
    
    foreach ($result as $record) {
      $context->data->cid = $record->cid;
      $context->data->title = $record->title;
      $context->data->description = $record->description;
    }
  }  else {
    $context->data->cid = null;
    $context->data->title = '';
    $context->data->description = '';
  }
  return $context;
}

For this example I have allowed writers to enter the name of a aggregator category in a text field and now in the context creation function we can use the value in the field to query the aggregator_category table to get the record matching the entered value. On a side not in order for this to function a validation of the entered value should be added to the field, to ensure that the entered value exists as a category.

It this context is used where there is no term available e.g. the panel page admin UI we will set the default cid to null and the other values to empty strings, this enables us to envoke the

When the filter value is not available

options in view and it should be noted that the function must always be able to run or you will get an error in the admin UI, which is also a reason for the fallback to a null value.

Once we have the record we can create and fill the appropriate values in our context data object. This values will be used later in the token function. It is also important to note that the keys for the values should correspond to the columns in the table since this enables you to use them as contextual filter values in views.

Next we will add the two token functions:

function custom_context_example_convert_list() {
  return array(
    'cid' => t('Feed term id'),
    'title' => t('Feed term title'),
    'description' => t('Feed term description'),
  );
}
 
function custom_context_example_convert($context, $type) {
  switch ($type) {
    case 'title':
      return $context->data->title;
    case 'description':
      return $context->data->description;
    case 'cid':
      return $context->data->cid;
  }
}

and that is it. Now you can use the context as in your panel pages to provide context based on dynamic values. I hope this helps some of you out there.

Fading suns munition

§
by
Jesper bisgaard

Ammunition

Item Tech Cost Ammo Ben. Reference
Arrowhead - Normal 0 1 wing - - 206 2nd Ed
Arrowhead - Jag 1 +1 firebird - - 206 2nd Ed
Arrowhead - Streamer 1 +1 firebird - - 207 2nd Ed
Arrowhead - Boomer 5 +5 firebirds - - 207 2nd Ed
Crossbow Bolt 1 1 crest - - 207 2nd Ed
Speargun - Normal 4 1 crest/canister - - 194 PC
Speargun - Backbarbed 4 1/canister - - 194 PC
Speargun - Concussion 4 2/canister - - 194 PC
Speargun - Injector 4 1/canister - - 194 PC
Splinter Gun - Normal 6 1 wing/shot - - 196 PC
Splinter Gun - Thorns 6 2/shot - - 196 PC
Splinter Gun - Fangs 6 7/shot - - 196 PC
Slug - .32 calibre 4 5 slugs/FB 1 per 3 clips - 209 2nd Ed.
Slug - .40 calibre 4 3 slugs/FB 1 per 3 clips - 209 2nd Ed.
Slug - .47 calibre 4 1/slug 1 per 3 clips - 209 2nd Ed.
Slug - 10 mm 4 2/slug 1 per 3 clips - 209 2nd Ed.
Slug - 13 mm 4 3/slug 1 per 3 clips - 209 2nd Ed.
Shot - 10 gage 4 2 loads/FB 1 per 3 clips - 209 2nd Ed.
Shot - 8 gage 4 2 loads/FB 1 per 3 clips - 209 2nd Ed.
Slappers 5 +3 firebirds - - 208 2nd Ed.
Needler 5 +3 firebirds - - 209 2nd Ed.
Sunder Slugs 6 +3 firebirds - - 209 2nd Ed.
Ukar Needler 7 25/shot - - -CotG
Vorox Claws 5 +2 firebirds - - 209 2nd Ed.
Blast Capsules 6 +6 firebirds - - 209 2nd Ed.

Armour (Primitive)

Item Tech Cost Ammo Ben. Reference
Abar Leaf 3 30 - 1 201 PC
Chainmail 1 20 (50 plastic) - 2 212 2nd Ed
Heavy Cloth 0 1 crest - - 211 2nd Ed
Half-Plate 2 30 (60 plastic) - 2 212 2nd Ed
Kadak 3 30 - 1 76 CotG
Leather Jerkin 1 5 - - 212 2nd Ed
Padded Clothing 0 1 wing - - 211 2nd Ed
Plate 3 40 (80 plastic) - 3 212 2nd Ed
Scale Mail 1 13 (20 plastic) - 1 212 2nd Ed
Studded Leather 1 8 (15 plastic) - - 212 2nd Ed

Armour (Advanced)

Item Tech Cost Ammo Ben. Reference
Armour Mesh Spacesuit 6 500 - 4 -
Spacesuit 5 100 - 1 212 2nd Ed
Synthsilk 6 300 - 7 212 2nd Ed
Stiffsynth 7 500 - 7 213 2nd Ed
Polymer Knit (Kevlar) 5 200 - 6 213 2nd Ed

Armour (Powered)

Item Tech Cost Ammo Ben. Reference
Adept Robes 7 -N/A - 20 213 2nd Ed
Blur Suit 7 500 - 8 202 PC
Ceramsteel 6 1000 - 12 213 2nd Ed
Chameleon Suit 7 300 - 7 202 PC
Gyllevhem 7 10 - 5 43 CotG
Morph Suit 8 10 - 10 202 PC
Null Atmo Survival Suit 7 200 - 5 119 PC
Psi Cloak 8 3 - 10 213 2nd Ed

Armour Modifiers

Item Tech Cost Ammo Ben. Reference
Flame Retardant 5 50 - - 193 2nd Ed
Frictionless Gel 7 500 - 10 213 2nd Ed
Spikes 2 20 - 1 201PC
Artefact Guns
Item Tech Cost Ammo Ben. Reference
Fusion Gun - 5000 - 20 198 PC
Neural Disrupter - 3000 - 10 198 PC
Symbiot Element Gun - Lightning - 5000 - 12 199 PC
Symbiot Element Gun - Hail - 5000 - 10 199 PC
Symbiot Element Gun - Wind - 5000 - 9 199 PC

Artefact Hand Cannons-

Item Tech Cost Ammo Ben. Reference
Laser Canon - 20000+ - - here
Plamsa Canon - 55000+ - - here
Sonic Canon - 40000+ - - here
Thunder Canon - 45000+ - - here
Null Canon - 60000+ - - here

Artefact Melee Weapons

Item Tech Cost Talon Ben. Reference
Chaos Sword 8 20,000+ - - here
Fire Sword 8 22,000+ - - here
Flux Sword 8 15000+ - 11 205 2nd Ed
Mist Sword 8 30,000+ - - 206 2nd Ed
Neural Whip 8 8000 - - here
Wireblade 8 10000 - 12 206 2nd Ed

Artillery (Primitive)

Item Tech Cost Ammo Ben. Reference
Ballista (Heavy) 1 100 large rocks - 121 LotE
Ballista (Light Field) 1 50 metal or wooden bolts - 121 LotE
Catapult 1 75 large rocks - 121 LotE
Spring Engine 1 70 large rocks - 122 LotE
Trebuchet 1 90 extremely large rocks - 122 LotE

Artillery (Advanced)

Item Tech Cost Ammo Ben. Reference
15mm Gun 4 1700 15/shell - 133 MotJW
90mm Gun 4 5000 100/shell - 125 LotE
Li Halan Light Mortar 4 700 explosive shells - 30 FL:T
The Kagor "Tusker" 76mm 3 2500 50/shell - 122 LotE
The L-66 "Wild Wombat" 5 6000 100/shell - 122 LotE
Decados 62cm Xf-123 "City Killer" 4 16000 250/shell - 122 LotE

Chemical Weapons

Item Tech Cost Ammo Ben. Reference
Aqua Ignata 3 varies - - 91 H&O
Flamegun 5 150 5/canister - 209 2nd Ed
Plague Bomb 7 2500 - - 31 FL:T
Sinners' Ash 6 varies - - 120 LotE
Xaos Gas 6 650 - - 31 FL:T

Energy Guns

Item Tech Cost Ammo Ben. Reference
Assault Laser- 6 700 10/Cel - 209 2nd Ed
Blaster Pistol 7 700 10/Cel - 209 2nd Ed.
Blaster Rifle 7 1000 10/Cel - 209 2nd Ed
Blaster Shotgun 7 1200 10/Cel - 209 2nd Ed
Eruptor Blast Pistol 7 700 10/Cel - 196 PC
Laser Pistol 6 300 10/Cel - 209 2nd Ed
Laser Rifle 6 500 10/Cel - 209 2nd Ed
Palm Laser 6 200 10/Cel - 209 2nd Ed
Microwave Gun 5 500 10/Cel. 10 119 LotE
Neural Disruptor 8 3000 10/Cel - 210 2nd Ed
Nitobi Blaster Axe 7 3000 - 10 196 PC
Radium Blaster 6 700 -. 10 120 LotE
Schreecher 6 300 10/Cel - 209 2nd Ed
Stunner 6 300 10/Cel - 209 2nd Ed

Energy Melee Weapons

Item Tech Cost Ammo Ben. Reference
Avestite Sword of Penance 5 75 fusion cel - 118 LotE
Chainblade 5 x10 - - here
Forcesticks 5 1500 - - here
Force Swords 6 x10 - - here
Frap Stick 4 15 - - 205 2nd Ed
Laser Staff 5 400 - - 82 A&D
Shocker 5 30 - - 205 2nd Ed
Vibrating Blade 5 100 - - 205 2nd Ed
Venlinni Laser 6 3000 - - 82 A&D

Energy Shields

Item Tech Cost Ammo Ben. Reference
Standard Shield - 500 - - 213 2nd Ed
Dueling Shield - 700-1000 - - 213 2nd Ed
Assault Shields - 3000+ - - 213 2nd Ed
Battle Shields - 5000+ - - 213 2nd Ed

Energy Support Weapons

Item Tech Cost Ammo Ben. Reference
Blaster Cannon 7 5000 custom fusion cel - 29 FL:T
Gatling Laser 6 2500 5 fusion cels - 28 FL:T
Mass Disrupter 8 25000 fusion cel - 29 FL:T

Explosives

Item Tech Cost Ammo Ben. Reference
Grenades 4 50 - - 211 2nd Ed
Wire Grenade 4 80 - - 211 2nd Ed
Blast Pellet 6 20-30 - - 211 2nd Ed
Plasma Grenade 6 100 - - 211 2nd Ed
Demolition Rig 4+ 200 - - 211 2nd Ed
Flash Grenade 5 70 - 1 197 PC
Frag Grenade 4+ 50+ - 1 197 PC
Mist Grenade 5 70 - 1 197 PC
Plasma Grenade 6 100 - 2 197 PC
Rad Baffler Grenade 7 200 - 2 197 PC
Redemption Mines 4+ -as normal grenade - - 120 LotE
Shock Grenade 5 90 - 1 197 PC

Flechette Guns

Item Tech Cost Ammo Ben. Reference
Splinter Pistol 6 70 1 wing/shot - 195 PC
Splinter Rifle 6 100 1 wing/shot - 195 PC

Heavy Weapons

Item Tech Cost Ammo Ben. Reference
Cluster Gun 6 100 blast pellets - 196 PC
Grenade Launcher 5 500 65/grenade - 210 2nd Ed
Furystorm Chaingun 5 1500 50/clip - 210 2nd Ed
Jahnisak Light Machinegun 4 750 10 mm - 210 2nd Ed
Jet Pistol 6 100 mini-rockets - 196 PC
Light Machinegun 5 750 10 mm - 210 2nd Ed
Missile Launcher 4 800 100/missile - 210 2nd Ed
Rocketeer 5 400 25/grenade - 210 2nd Ed
Terrakin Borer 5 1000 5/slug - 120 LotE

Ground to Space Ordinance

Item Tech Cost Ammo Ben. Reference
Bresno MkV "Pom-Pom" Laser 5 25000 - - 123 LotE
Falling Star Cannon 6 48000 - - 123 LotE

Laser Mounts

Item Tech Cost Ammo Ben. Reference
Bayonet Mount 7 200 - - 81 A&D
Frequency Variable 7 200 - - 81 A&D
Intensifier 6 100 - - 81 A&D
Sapper 6 70 - - 81 A&D
Overcharger 7 500 - - 82 A&D
Parabolic Mortar 7 500 - - 82 A&D

Melee Weapons

Item Tech Cost Ammo Ben. Reference
Axe - 5 - - 204 2nd Ed
Bootknife 3 15 - - 119 LotE
Braceblade 5 x3 - - here
Broadsword - 15 - - 204 2nd Ed
Cestus 1 1 - - 119 LotE
Club - 1/3/5 talons - - 204 2nd Ed
Dirk - 4 - - 203 2nd Ed
Flail - 4 - - 204 2nd Ed
Fleshblade 5 x2 - - here
Garrote - 5 - - 204 2nd Ed
Glankesh Sword - 15 (25 non-Vorox) - 204 2nd Ed
Glassblade 5 x5 - - here
Katana - 20 - - 204 2nd Ed
Knife - 2 - - 203 2nd Ed
Kurgan Side Sword 5 400 - - 191 PC
Mace - 10 - - 204 2nd Ed
Polyblade 5 20 - - here
Rapier - 10 - - 203 2nd Ed
Razroblade 5 x7 - - here
Scimitar - 20 - - 204 2nd Ed
Shadowblade 5 x2.5 - - here
Spear - 1 - - 204 2nd Ed
Splinter Sword 6 50 - - 193 PC
Spring Knife - 5 - - 203 2nd Ed
Staff - 1/3/5 talons - - 204 2nd Ed
Suresnake Whip - 100 - - 204 2nd Ed
Trench Knife 1 6 - - 119 LotE
Two-handed Sword - 30 - - 204 2nd Ed
Whip - 3 - - 204 2nd Ed
Ranged Weapons (Bows)
Item Tech Cost Ammo Ben. Reference
Hunting Bow - 5 arrows - 206 2nd Ed
Long Bow (Military Bow) - 10 arrows - 206 2nd Ed
Target Bow - 7 arrows - 206 2nd Ed
Thungari (Vorox double-Bow) 4 25 1crest/arrow - 118 LotE

Ranged Weapons (Crossbows)

Item Tech Cost Ammo Ben. Reference
Hand Crossbow 2 7 3 wings/bolt - 207 2nd Ed
Heavy Crossbow 2 15 1 crest/bolt - 207 2nd Ed
Medium Crossbow 2 10 1 crest/bolt - 207 2nd Ed

Ranged Weapons (Misc)

Item Tech Cost Ammo Ben. Reference
Heavy Crossbow 4 1525 1 wing/canister - 194 PC
Ring Dart 7 150 5/dart - 123 S&R
Ring Laser 7 400 20/cel - 123 S&R
Slingshot 0 1-2 - - 194 PC

Restraint Weapons

Item Tech Cost Ammo Ben. Reference
Grappler 6 70 Samson Hair Wire - 198 PC
Stun Net 5 200 fusion cel - 198 PC
Taffy Gun 7 70 foam glue - 198 PC

Shields

Item Tech Cost Ammo Ben. Reference
Buckler 1 7 - - 213 2nd Ed
Buckler (Plastic) 5+ 12 - - 213 2nd Ed
Light Shield 1 15 - - 213 2nd Ed
Light Shield (Plastic) 5+ 20 - - 213 2nd Ed

Slug Guns

Item Tech Cost Ammo Ben. Reference
Assault Rifle - - 10 mm - 208 2nd Ed
Decados Groin Gun 5 30 .32 calibre - 194 PC
Derringer - 50 .32 calibre - 207 2nd Ed
Drexler Gatling Shotgun 5 50 8 gauge - 194 PC
Heavy Autofeed (.42) - 300 .47 calibre - 207 2nd Ed
Heavy Revolver (.42) - 250 .47 calibre - 207 2nd Ed
- Imperial Rifle - 200 .40 calibre - 207 2nd Ed
Light Autofeed (.32) - 150 .32 calibre - 207 2nd Ed
Light Revolver (.32) - 100 .32 calibre - 207 2nd Ed
Medium Autofeed (.40) - 250 .40 calibre - 207 2nd Ed
Medium Revolver (.40) - 200 .40 calibre - 207 2nd Ed
Mitchau Quarry Gun 5 50 8 gauge - 194 PC
Shotgun - 300 shot - 208 2nd Ed
Sniper Rifle - 700 13mm - 208 2nd Ed
Submachine Gun - 350 .40 calibre - 208 2nd Ed

Slug Gun Accessories

Item Tech Cost Ammo Ben. Reference
Boarding Gunknife 3 10 - - 193 2nd Ed
Silencer - 5 - - 208 2nd Ed
Shantor Gun Mount (Fixed) 4+ x1.5 - - 117 PC
Shantor Gun Mount (Remote-Fired) 5+ x2 - - 118 PC
Shantor Gun Mount (Swivel) 5+ x3 - - 118 PC
Shantor Gun Mount (Targeting Helmet) 6 200 - - 118 PC

Thrown Weapons

Item Tech Cost Ammo Ben. Reference
Dart - 2 each - - 206 2nd Ed
Knife - 2 each - - 206 2nd Ed
Star - 2 each - - 206 2nd Ed

Military Vehicles

Item Tech Cost Ammo Ben. Reference
al-Malik Flying Crescent 7 75000 fusion generator - 124 LotE
Decados Su-325 "Black Adder" Hovertank 6 45000 fusion - 125 LotE
Hazat Cx-Omega "Mini-Mite" 4 12000 fusion - 124 LotE
Imperial Kestrel 6 45000 fusion - 125 LotE
Li Halan Mantius "Cursader-X" 5 3000 fusion - 125 LotE
Pitbull Medium Tank 5 3000 90mm Gun - 125 LotE
Pherizas - 3000 - - 226 2nd Ed
Warhorse - 5000 - - 225 2nd Ed

-