Excerpt taken from an older project. Initializes an Octree which is later used to form a hierarchy of the scene geometry.
/*////////////////////////////////////////////////////////////////////////////////
Name: Build_Recursive
Description: Recursively builds an Octree containing the scene geometry.
Called after the root node/geometry has been initialized.
////////////////////////////////////////////////////////////////////////////////*/
void Octree::Build_Recursive(OCT_Node* self, int depth)
{
if (self == NULL) return;
// Exit conditions
if (self->geometry.size() <= TREE_MIN_GEOMETRY) return;
if (depth >= TREE_MAX_DEPTH) return;
// Make child nodes
D3DXVECTOR3 offset;
float step = self->halfWidth / 2.0f;
for (int i = 0; i < 8; ++i)
{
self->pChild[i] = new OCT_Node();
self->pChild[i]->halfWidth = step;
// make children by splitting the tree cell along
// x/y/x axes in positive/negative directions, alternating
offset.x = ((i & 1) ? step : -step);
offset.y = ((i & 2) ? step : -step);
offset.z = ((i & 4) ? step : -step);
self->pChild[i]->center = self->center + offset;
// try to avoid too many extra vector resizings when
// inserting node geometry
self->pChild[i]->geometry.reserve(TREE_MIN_GEOMETRY);
// set node bounding box and draw color, for debugging
self->pChild[i]->MakeBox();
self->pChild[i]->color = octree_colors[depth+1];
}
// Assign triangles to child nodes
// Simple brute-force 'Triangle-In-Node(AABB)?' test
for(int i = 0; i < self->geometry.size(); ++i)
{
for(int j = 0; j < 8; ++j)
{
if ( self->pChild[j]->InNode(self->geometry[i]) )
self->pChild[j]->geometry.push_back( self->geometry[i] );
}
}
self->geometry.clear();
// Subdivide each new node recursively
for (int i = 0; i < 8; ++i)
Build_Recursive(self->pChild[i], depth+1);
}
A portion of the code that handles generating and rendering a Fog of War effect.
This is currently being used in the Tower project.
/**
* A class that handles displaying the fog of war effect.
* @class Knight.Fog_Display
*/
Knight.Fog_Display = class extends PIXI.Container {
/**
* Creates the object. Caller is responsible for attaching it to the current Scene.
*/
constructor() {
super();
this._graphics = new PIXI.Graphics();
this._tileWidth = $gameMap.tileWidth();
this._tileHeight = $gameMap.tileHeight();
this._dirty = true;
if (Knight.FOW.USE_BLUR) {
const blurFilter = new PIXI.filters.BlurFilter(
Knight.FOW.BLUR_STRENGTH,
Knight.FOW.BLUR_QUALITY,
1,
5);
blurFilter.repeatEdgePixels = true;
this.filters = [blurFilter];
// Filter area has to be manually assigned
this.filterArea = Graphics._renderer.screen;
}
this.addChild(this._graphics);
}
/**
* Draws a black tile over every map tile that is not visible. Does not draw
* offscreen tiles, so it should be called whenever the map moves.
*/
drawMap() {
this._graphics.clear();
const fogData = $gameFogOfWar.data();
const displayX = $gameMap.displayX();
const displayY = $gameMap.displayY();
const startX = Math.max(Math.floor(displayX) - 1, 0);
const startY = Math.max(Math.floor(displayY) - 1, 0);
const endX = Math.min(Math.ceil(displayX) +
Math.ceil($gameMap.screenTileX()) + 1, fogData.width());
const endY = Math.min(Math.ceil(displayY) +
Math.ceil($gameMap.screenTileY()) + 1, fogData.height());
this._graphics.beginFill(0x000000, 1);
for (let j = startY; j < endY; ++j) {
for (let i = startX; i < endX; ++i) {
if (!fogData.is_visible(i, j)) this._drawTile(i, j);
}
}
this._graphics.endFill();
this._dirty = false;
}
/**
* Renders a tile at a given position, using current settings on the
* internal graphics object. Should be called between .beginFill()
* and .endFill() calls.
*
* @param {Number} x
* @param {Number} y
*/
_drawTile(x, y) {
const dx = x * this._tileWidth;
const dy = y * this._tileHeight;
this._graphics.drawRect(dx, dy, this._tileWidth, this._tileHeight);
}
/**
* Reveals tiles within a character's visibility radius, described by the input grid.
* @param {BitpackedGrid} grid Character's visibility grid. Should be square.
* @param {Number} x Character's X position in tile coordinates
* @param {Number} y Character's Y position in tile coordinates
*/
projectViewer(grid, x, y) {
const fogData = $gameFogOfWar.data();
const mapWidth = $gameMap.width();
const mapHeight = $gameMap.height();
const gridSize = grid.width();
const gridHalfSize = 0.5 * (gridSize - 1);
for (let j = 0; j < gridSize; ++j) {
for (let i = 0; i < gridSize; ++i) {
// Only reveal tiles we can newly see, don't hide already discovered tiles
if (grid.is_visible(i, j)) {
const tileX = (x + i - gridHalfSize).clamp(0, mapWidth-1);
const tileY = (y + j - gridHalfSize).clamp(0, mapHeight-1);
fogData.set_visible(tileX, tileY, true);
}
}
}
this._dirty = true;
}
/**
* Frame update. Updates coordinates and redraws the map if necessary.
*/
updateTransform() {
this._graphics.position.x = -($gameMap.displayX() * $gameMap.tileWidth());
this._graphics.position.y = -($gameMap.displayY() * $gameMap.tileHeight());
PIXI.Container.prototype.updateTransform.call(this);
if (this._dirty) this.drawMap();
};
};
Excerpt from an older, discontinued Ruby project, showing a basic pathfinding query using Dijkstra's algorithm.
Supporting data structures and helper classes omitted for brevity.
Supporting data structures and helper classes omitted for brevity.
#==============================================================================
# â– Combat_Pathfinder
#------------------------------------------------------------------------------
# Implementation of pathfinding using a modified version of Dijkstra's
# algorithm. Calculates the minimum cost path from a single node to
# every other node in a grid.
#==============================================================================
# Initialization/Boilerplate code omitted for brevity
#--------------------------------------------------------------------------
# make_path_map
# Makes a "path map" structure, which stores the minimum cost path
# from the start node to every other node. This is the main part
# of the algorithm.
#--------------------------------------------------------------------------
def make_path_map(start_x, start_y)
# Initialize Priority Queue
@p_queue = PQueue.new( proc{|a,b| a.min_cost < b.min_cost} )
# Initialize start node
@nodes[start_x][start_y].min_cost = 0
@p_queue.push( @nodes[start_x][start_y] )
while !@p_queue.empty?
# Pop node from PQ, set to cur_node
cur_node = @p_queue.pop
# For each neighbor n of cur_node
for dir, n in NEIGHBORS
# If n exists, is not done and is a passable map tile
next if !node_exists?(cur_node.x + n[0], cur_node.y + n[1])
n_node = @nodes[cur_node.x + n[0]][cur_node.y + n[1]]
next if n_node.done
# Check tile passability, skip tiles player can't walk on
next unless $game_player.passable?(cur_node.x, cur_node.y, dir)
# Update n's cost (current node's cost + neighbor's edge cost)
new_cost = cur_node.min_cost + n[2]
# If new cost is better
if new_cost < n_node.min_cost
# Replace old cost, update direction
n_node.min_cost = new_cost
n_node.came_from = PathNode::DIR_REVERSE[ dir ]
# update priority queue
if @p_queue.exists?( n_node )
@p_queue.make_legal # Node is already in there, just re-sort
else
@p_queue.push( n_node )
end
end
end
# Set cur_node to done
cur_node.done = true
end
end
A variety of snippets of user-defined Lua code used in a modified version of an existing plugin for certain current MMORPG.
Contains a bunch of utility functions to manage the player's equipment under various conditions.
Contains a bunch of utility functions to manage the player's equipment under various conditions.
-------------------------------------------------------------------------------------------------------------------
-- Setup functions for this job.
-------------------------------------------------------------------------------------------------------------------
function job_pretarget(spell, spellMap, eventArgs)
if spell.action_type == 'Ranged Attack' and (player.equipment.ammo == 'Togakushi Shuriken') then
cancel_spell()
add_to_chat(123,'Abort: Don\'t throw your good ammo!')
elseif spell.name == 'Sange' and (player.equipment.ammo == 'Togakushi Shuriken') then
cancel_spell()
add_to_chat(123,'Abort: Don\'t throw your good ammo!')
end
end
function job_precast(spell, action, spellMap, eventArgs)
-- Use Echo Drops when silenced
if spell.action_type == 'Magic' and buffactive['Silence'] then
cancel_spell()
send_command('input /item "Echo Drops" ')
end
end
-- Run after the general midcast() is done.
-- eventArgs is the same one used in job_midcast, in case information needs to be persisted.
function job_post_midcast(spell, spellMap, eventArgs)
if spellMap == 'ElementalNinjutsu' then
if spell.english:contains(': San') then
equip({head="Mochi. Hatsuburi +1"})
end
if state.MagicBurstMode.value ~= 'Off' then
equip(sets.MagicBurst)
end
if state.Buff.Futae then
equip(sets.precast.JA['Futae'])
end
if spell.element == world.weather_element or spell.element == world.day_element then
equip({waist="Hachirin-no-Obi"})
if state.CastingMode.value == 'Normal' or state.CastingMode.value == 'Fodder' then
if spell.element == world.day_element then
if item_available('Zodiac Ring') then
sets.ZodiacRing = {ring2="Zodiac Ring"}
equip(sets.ZodiacRing)
end
end
end
end
if spell.element and sets.element[spell.element] then
equip(sets.element[spell.element])
end
end
end
-------------------------------------------------------------------------------------------------------------------
-- Job-specific hooks for non-casting events.
-------------------------------------------------------------------------------------------------------------------
-- Called when a player gains or loses a buff.
-- buff == buff gained or lost
-- gain == true if the buff was gained, false if it was lost.
function job_buff_change(buff, gain)
update_melee_groups()
buff_lower = buff:lower()
if hastetbl:contains(buff_lower) then
check_haste_level()
handle_equipping_gear(player.status)
elseif state.Buff[buff] ~= nil then
handle_equipping_gear(player.status)
end
end
function job_status_change(new_status, old_status)
add_to_chat(122,'job_status_change, ' .. new_status)
if new_status == 'Idle' then
select_movement_feet()
end
end
-------------------------------------------------------------------------------------------------------------------
-- Utility functions specific to this job.
-------------------------------------------------------------------------------------------------------------------
function is_night_time()
return (world.time >= 17*60) or (world.time < 7*60)
end
function select_movement_feet()
add_to_chat(122,'select_movement_feet')
if is_night_time() then
gear.MovementFeet.name = gear.NightFeet
else
gear.MovementFeet.name = gear.DayFeet
end
end
-- Remaining code omitted for brevity...