#!/usr/bin/env brick-tcl
# beworld - Prototype Brick Engine game
# License: Creative Commons (see license information section)
# Revision: 211113
#---------------------------------------------------------------------
# License information.
# This software is distributed under the following license: Creative
# Commons Attribution-NonCommercial-ShareAlike 3.0. For more informa-
# tion, visit:
#
# http://creativecommons.org/
#---------------------------------------------------------------------
# Important note.
# This software is provided on an as-is basis with no warranty. The
# entire risk as to the quality and performance of the software is
# with you. Should the software prove defective, you assume the cost
# of all necessary servicing, repair or correction. In no event will
# any of the developers, or any other party, be liable to anyone for
# damages arising out of use of the software, or inability to use the
# software.
#---------------------------------------------------------------------
# Documentation: Changes in revision 110923.
# Brief change summary:
# Brick Engine 5.4 or above is now supported (and required, though
# older APIs may be used for testing purposes).
# Multi-frame sprites are now supported, both for multi-shape classes
# and for animation purposes. In the multi-shape case, shapes may be
# selected either by name or randomly.
# Added drop shadows, a "God" mode power-up, and new objects: cars,
# dogs, pigs, and tigers. Added an animated "fire" level. The game no
# longer needs OpenGL. There is now a goal: Make it to the exit. Ei-
# ther Control or Space may be used to shoot. License has changed to
# Creative Commons.
# More object properties are supported. For example, objects may now
# have random sizes that fall into a pre-defined range. Attacks by the
# player may affect them differently, attacks by them may affect the
# player differently, and they may make different sounds under differ-
# ent circumstances.
# Details:
# (a) Ported the program to the Brick Engine 5.4 API.
# (b) The player may now use either Left-Control or Space to shoot.
# (c) For best results, the following "cmake" rule should be used when
# Brick is built:
#
# -DCMAKE_BUILD_TYPE:STRING=RELEASE
#
# Note: This will help other Brick programs as well as BEWorld.
# (d) A "God mode" power-up feature is supported. The player sprite
# used in "God mode" and the amount of time that "God mode" lasts are
# both configurable.
# (e) Drop shadows are now supported. This feature may be enabled or
# disabled for objects on a per-class and/or per-world basis.
# (f) OpenGL is no longer needed. Non-OpenGL mode is now the default
# (though OpenGL is still supported). However, the "cmake" rule men-
# tioned previously is important. Brick should be built in RELEASE
# mode or BEWorld will be too slow.
# (g) Objects may now be assigned random sizes taken from a specified
# range. The range may be set on a per-class and/or per-world basis.
# (h) Multi-frame sprites are now supported, both for multi-shape
# classes and for animation purposes. In the multi-shape case, shapes
# may be selected either by name or randomly.
# (i) Objects may now make different sounds in different circumstan-
# ces. For example, different sounds may be set for object hit by wea-
# pon and object destroyed by weapon. The sounds in question may be
# set on a per-class and/or per-world basis.
# (j) Additional parameters may now be set on a per-class and/or per-
# world basis, including:
#
# Initial health of an object
# Health effect on an object per attack by the player
# Health effect on the player per attack by an object
# Score change if an object is destroyed by shooting
# Distance from player at which an object becomes cautious
# Whether or not an object can be attacked
# (k) Added tigers. They hide behind trees until the player happens to
# pass close by. Tigers are similar to the older enemy types but are a
# little tougher; they can be destroyed by shooting, but more bullets
# are required.
# (l) Added dogs and pigs. Pigs are simply variations on cows. Dogs
# follow the player and are friendly. They'll fight tigers on his/her
# behalf (they're evenly matched).
# (m) Money now has different forms, is mobile, and is reluctant to be
# picked up.
# (n) The red-square enemies are now known as Karkinos. They may be
# thought of as a type of crab; the crab constellation Cancer is based
# on the original Karkinos legend.
# (o) Added cars. The player can drive a car; while doing so, he/she
# is safe from some attacks but can't shoot (attempts to shoot will
# simply beep the car's horn).
# (p) Added a global parameter BRICKAPI that specifies the Brick API
# to use (5200 for Brick 5.2, 5300 for Brick 5.3, and 5400 for Brick
# 5.4).
# For the time being, the program should run under any of the support-
# ed APIs if BRICKAPI is set correctly. However, there are some cav-
# eats:
#
# Older APIs are only supported for testing purposes. When an older
# API is used, some features will be omitted and others will be only
# partly supported.
#
# If Brick 5.2 is used, a "wrap_sprite_position" patch (available sep-
# arately) must be applied to the Brick sources and Brick must be re-
# built. The patch isn't needed for Brick 5.3 or above.
#---------------------------------------------------------------------
# Documentation: Changes in revision 101010.
# (a) Added a preliminary "help" screen; it can be accessed using the
# "h" key.
# (b) Added the start of a player inventory (or backpack). Added an
# associated "inventory" screen, which can be accessed using the "i"
# key.
# (c) Reorganized the information display. It has two columns now, and
# a message line has been added.
# (d) Added cows. Presently, cows are simply grazers who happen to eat
# red enemies.
# (e) Added trees. The player can hide behind trees, but only once per
# tree and shooting from there breaks cover.
# (f) Added money. Money simply goes into the player's inventory;
# there's no way to spend it yet.
# (g) New subroutine keywords: "dmproc" and "xproc". "dmproc" extends
# "proc" and adds some debugging features. "xproc" extends "proc" and
# adds support for call-by-reference.
# (h) New sound effects: "moo" and "bonus" (played when a health or
# financial bonus is received).
# (i) Added support for a new data compression format (bxdiv). It's
# intended primarily for use with sound data.
# (j) For some object classes (including cows, medical kits, and red
# enemies), object are now given individual names.
# (k) Most object-related parameters can now be set using either glo-
# bal defaults or per-world values.
# (l) The main sprite prototype setup routine (make_proto_sprite) now
# supports up to 64 colors per sprite.
# (m) Added more documentation.
#---------------------------------------------------------------------
# Documentation: Changes in revision 101003.
# (a) Notable user-visible items added (summary): Multiple worlds and
# inter-world portals. Scrolls. Variable-size enemies. Variable-size
# random maps. Limbo (a world that has no map, walls, or floor and
# that wraps around). Caspak (a world that's simple but not very
# friendly).
# (b) Notable internal features added (summary): "base64" decoding.
# LZ77 decompression. Background music is now built-in. Sprites can
# be drawn as simple text diagrams. It's easy to add new worlds and to
# connect them in different ways. A specified number of enemies may be
# preloaded.
# (c) "base64" decoding: Pure-Tcl support for "base64" decoding has
# been added. No external programs or Tcl modules are required for de-
# coding.
# (d) LZ77 decompression: Pure-Tcl support for LZ77 decompression has
# been added. No external programs or Tcl modules are required for de-
# compression.
# The LZ77 feature may be used in conjunction with the "base64" fea-
# ture to load binary data from a compressed but ASCII-encoded repre-
# sentation.
# Note: If you'd like to add new compressed objects to this program,
# you'll need to use a separate LZ77 compression tool named "lzbe-
# tool". "lzbetool" is a short pure-Tcl script that should be availa-
# ble from the same place as this program.
# (e) Music: Background music is now built into the program; it's rep-
# resented as LZ77-compressed and base64-encoded inline data. No ex-
# ternal music file is required. However, external music files are
# still supported. For more information, see PlayMusic and MusicFile.
# Additional minor change: The MusicVolume setting is now optional.
# (f) Sound effects: Sound effects have switched from simple "hex" en-
# coding to LZ77-compressed and base64-encoded blocks; this makes them
# significantly smaller.
# (g) Multiple worlds: The program now supports multiple rooms (or
# worlds).
# Two types of inter-world portals have been added: "forward" and "re-
# verse". These are similar to Netscape "down" and "up" stairs, but
# they can be used more generally. For more information, see the docu-
# mentation section named "Inter-world portals".
# The multiple-worlds feature works both in random-map mode (with
# RandomMapEnable set to one) and in invariant-map mode (with the par-
# ameter in question set to zero). However, in invariant-map mode, the
# coder must pre-define an invariant map for every world that's going
# to be used.
# (h) Scrolls: The game now includes scrolls. Presently, these are
# "demo" objects that simply display random quotes (or fortunes). The
# number of scrolls may be specified on a per-world basis. It may be
# set to zero, a fixed number, or a range.
# (i) Intra-world portals: The number of intra-world portals may now
# be set on a per-world basis. It may be set to zero, a fixed number,
# or a range.
# (j) Enemies: Three different red-enemy sizes are now supported. The
# size used may be set on a per-world basis. Enemies may be preloaded;
# i.e., generated as part of world creation. The number of enemies to
# preload may be set on a per-world basis.
# (k) Info display: The info display has been moved to its own
# (transparent) layer. The code tries to keep the info-display layer
# on top. The width of the info display is now parameterized. The info
# display now includes the name of the current world.
# (l) Sprite definitions: It's easy to define sprites images now;
# they're drawn as inline text. For the time being, all sprites have
# switched to the inline-text approach. Some sprites may use other
# approaches in the future. For more information about the new conven-
# tions, see "make_proto_sprite".
# (m) Random maps: Random maps have variable dimensions now. Addition-
# ally, some bugs in the random-map generator have been fixed.
# (n) Message screens: The arrow keys may now be used to close message
# boxes (such as those created for the startup message and/or
# scrolls). The Escape and Q keys still work, as well.
# (o) Miscellaneous: Most of the code has been moved into subroutines.
# A fair amount of documentation has been added. Additionally, the
# program now includes "debug" code. The "debug" code is controlled by
# a new parameter named DebugLevel.
# There's now a configurable upper limit on the maximum number of bul-
# lets that can exist simultaneously in a given level. The limit is
# needed because of the way that Limbo works; without a limit, the
# number of bullets might increase indefinitely.
# The B.E. text-string engine seems to limit strings to a few lines'
# worth of text. The program's main message-display routine (presently
# named "show_msg") has been modified to work around the limitation.
# The new version breaks messages up into lines and displays the lines
# separately.
#---------------------------------------------------------------------
# Documentation: Changes in revision 100923.
# Random game maps are now supported
# Maps are no longer limited to a single size
# Added optional medical kits
# Medical kits are alive; the player needs to catch them
# Added optional teleportation portals
# Sprite creation frequency is now configurable
# Maximum number of sprites may now be capped
# Added chroma-key setup code
# Sprite colors are now configurable
# Sprites now move in 8 directions as opposed to 4
# Reformatted sprite creation code to make layout visible
# Reformatted default game map to make layout visible
# Documented "hex" approach to raw-sound operations
#---------------------------------------------------------------------
# Documentation: Changes in revision 100922.
# (a) As of this revision, if Brick 5.2 is used, the "wrap_sprite_
# position" patch discussed elsewhere is required. The patch is not
# needed for newer releases of Brick.
# (b) "Hunter" enemies have been implemented.
# (c) The default enemy speed is now configurable. Additionally, dif-
# ferent enemies may have different speeds.
#---------------------------------------------------------------------
# Documentation: Changes in revision 100921.
# Optional background music is now supported
# Now supports systems without OpenGL
# Statistics are now displayed in a rectangular box
#---------------------------------------------------------------------
# Documentation: Changes in revision 100920a.
# Added a program parameters section
# Added a sound effect for enemies popping (when they're shot)
# Added a sound effect for getting hit by enemies
# Can now quit from the opening screen
# "Q" key now quits (as well as Escape key)
# Opening message is now displayed in a rectangular box
# Revised some of the program comments
#---------------------------------------------------------------------
# Changes in revision 100920.
# Ported program to Brick Engine 5.2 API
#---------------------------------------------------------------------
# Documentation: Terminology.
# This isn't an object-oriented program in the standard sense, but it
# uses objects and classes in a manner of speaking.
# A object class is something for which two routines "new_NAME" and
# "run_NAME" exist (NAME being the name of the class). "new_NAME" cre-
# ates an instance of the class. "run_NAME" is called subsequently
# (and repeatedly) for each instance to initiate and/or direct actions
# by the instance.
# An object-class parameter is any program parameter that's tied to a
# particular object class.
#---------------------------------------------------------------------
# Documentation: Object-class parameters.
# 1. Most object-class parameters may be set either as global defaults
# or on a per-world basis.
# 2. Global defaults are defined as follows:
#
# set gdata(Default_CLASS_PARAM) ...
#
# where CLASS is an object-class name (ocbullet, ocscroll, octree,
# etc.) and PARAM is a parameter name (divmin, frequency, etc.).
# 3. To override a given parameter for a given world, add a statement
# similar to the following at the appropriate location in the world's
# definition section:
#
# set gdata($World.CLASS_PARAM) ...
#
# where, again, CLASS is an object-class name and PARAM is a parameter
# name.
# For example, there are no cows in Caspak. For cows, the object-
# class name "occow" is used. Therefore, to keep cows out of Caspak,
# the following statement has been added to Caspak's world-definitions
# section:
#
# set gdata($World.occow_maxnum) 0
# 4. Some of the most frequently used object-class parameters are dis-
# cussed in the following sections.
#---------------------------------------------------------------------
# Documentation: Object-creation modes.
# From an object-creation perspective, object classes are classified
# as "periodic", "upfront", or "unique".
# For "periodic" classes, the associated "new_" routines are called
# directly by the main loop in "main_routine". The routines may also
# be called elsewhere.
# For "upfront" classes, the associated "new_" routines are called
# indirectly by "make_world" when a world is created; specifically,
# through the loop in "make_world" that invokes "make_upfront". The
# routines may also be called elsewhere.
# For "unique" objects, the associated "new_" routines are called
# neither by the main loop in "main_routine" nor by the "make_upfront"
# loop in "make_world".
#---------------------------------------------------------------------
# Documentation: More about "periodic" classes.
# If an object class falls into the "periodic" category, the associ-
# ated "new_" routine adds new instances periodically (on a random
# basis). Additionally, when a world is created, "make_world" may
# preload a specified number of instances.
# New instances of "periodic" classes may also be created due to spe-
# cial-case operations.
# There's a per-class parameter for classes of this type named
# "preload". This parameter may be set for a given class on a global
# and/or per-world basis. It specifies the number of instances of the
# class which should be added initially when a world is created.
# There's also a per-class parameter named "frequency" which controls
# the rate at which new instances are added by a given class's "new_"
# routine. This parameter should be a real number from 0.00000 to
# 0.10000. Use smaller numbers to decrease the creation rate and
# larger numbers to increase it.
# Additionally, there's a per-class parameter named "maxnum" which
# limits the maximum number of instances per world. Note: "maxnum"
# takes precedence over "preload".
# For some periodic-generation routines, setting the following global
# flag to one will override "frequency" once (after that, the flag is
# reset):
#
# gdata(force_create)
#
# Note: This flag overrides "frequency", but not "maxnum".
# "periodic" class names should be added to the following list:
#
# list_classes_periodic
# Examples of "periodic" classes include:
#
# occow, ockarkinos, ocmedical
#---------------------------------------------------------------------
# Documentation: More about "upfront" classes.
# If an object class falls into the "upfront" category, "make_world"
# adds zero or more instances to a world when the world is created.
# Subsequently, new instances are added only as the result of special
# circumstances.
# For this category, the per-class parameters "minnum" and "maxnum"
# specify the minimum and maximum number of instances per world, re-
# spectively.
# To disable a given "upfront" class for a given world, set "maxnum"
# to zero for the class in that world. To make the number of instances
# a fixed value, set both "minnum" and "maxnum" to the value in
# question.
# To let the program choose the number of instances from a range, set
# "minnum" to the first number in the range and set "maxnum" to the
# last number.
# "upfront" class names should be added to the following list:
#
# list_classes_upfront
# Examples of "upfront" classes include:
#
# ocintra, ocscroll, octree
#---------------------------------------------------------------------
# Documentation: More about special-case creation.
# Object classes that fall into the special-case creation category in-
# clude:
#
# ocbullet - bullets
# ocinter - inter-world portals
# ocplayer - player
#
# These are neither "periodic" nor "upfront" classes.
#---------------------------------------------------------------------
# Documentation: Bullets.
# Class name: ocbullet
# Sprite prototype(s): ocbullet
# Creation mode: Special
# "maxnum" shouldn't be set much higher than 30 for the "ocbullet"
# class. If it's set too high, problems may occur in Limbo or similar
# special-case levels.
#---------------------------------------------------------------------
# Documentation: Object speed.
# For mobile objects, "divmin" and "divmax" usually affect object
# speed. These two parameters set minimum and maximum "speed divis-
# ors", respectively.
# Smaller speed divisors result in faster sprites. Larger speed divi-
# sors reduce sprite speed. Values of about 3 to 5 produce average
# speeds. Presently, the minimum value supported is 1. This value pro-
# duces the maximum speed.
#---------------------------------------------------------------------
# Documentation: Inter-world portals.
# Class name: ocinter
# Sprite prototype(s): forward, reverse
# Creation mode: Special
# 1. There's two types of inter-world portals: "forward" and "re-
# verse". These are similar to Nethack-style "down" and "up" stairs,
# respectively, and games derived from this one may use them in the
# same way. However, this interpretation is an oversimplification;
# "forward" and/or "reverse" portals may lead to either "higher" or
# "lower" levels, or to different worlds located on the same level.
# Each "forward" portal is associated with a destination world name
# and a "reverse" portal located in the specified world.
# Each "reverse" portal is associated with a destination world name
# and a "forward" portal located in the specified world.
# When a world is created (by "make_world"), a sprite is added for
# each "forward" portal that it contains. No "reverse" portals are
# created, initially.
# When the player enters a "forward" portal, the associated destina-
# tion world is created (unless it already exists) and he/she is
# transported to the world in question. The transport process adds a
# "reverse" portal to the destination world that is connected to the
# "forward" portal used, unless this was done previously (as part of
# an earlier transport).
# When the player arrives at a destination portal, the portal is lock-
# ed until he/she steps off of it. This prevents infinite transport
# loops.
# 2. Each world-definition section includes a block similar to the
# following. The block defines a "to_worlds" list:
#
# set gdata($World.to_worlds) [list \
# $gdata(WorldElysian) \
# $gdata(WorldEternia) \
# ]
# To define the "forward" portals associated with a given world, set
# the contents of "to_worlds" to the names of the worlds that the
# world in question connects to. Use $gdata(World...) strings as shown
# here.
# If a given world doesn't have any "forward" connections to other
# worlds, use:
#
# set gdata($World.to_worlds) [list]
# 3. There's no need to define "reverse" portals (or any way to do
# so explicitly). The program creates "reverse" portals automatically
# as necessary.
#---------------------------------------------------------------------
# Documentation: Intra-world portals.
# Class name: ocintra
# Sprite prototype(s): ocintra
# Creation mode: upfront
# The number of intra-world portals in a given world may be set using
# either global defaults or per-world values. The relevant parameters
# are:
#
# ocintra_minnum = Minimum number of intra-world portals
# ocintra_maxnum = Maximum number of intra-world portals
# For an explanation of how parameters such as these are set (globally
# or per world), see the preceding sections.
#---------------------------------------------------------------------
# Documentation: Scrolls.
# Class name: ocscroll
# Sprite prototype(s): ocscroll
# Creation mode: upfront
# 1. The number of ocscrolls in a given world may be set using either
# global defaults or per-world values. The relevant parameters are:
#
# ocscroll_minnum = Minimum number of ocscrolls
# ocscroll_maxnum = Maximum number of ocscrolls
# For an explanation of how parameters such as these are set (globally
# or per world), see the preceding sections.
# 2. To change the contents of the ocscrolls, replace the contents of
# "wisdom_lz77_base64". The item in question should contain a base64-
# encoded text version of an LZ77-compressed copy of a "fortunes"
# file. The "fortunes" file should be structured as follows:
#
# Text for a fortune (may be multi-line)
# %%
# Text for another fortune
# %%
#
# etc. In other words, the file should contain one or more blocks of
# text, and each block should end with a line that contains just "%%".
# Lines should be no longer than 36 characters, excluding newline
# characters.
# Note: To LZ77-compress the file, use the "lzbetool" program mention-
# ed previously.
#---------------------------------------------------------------------
# Documentation: Trees.
# Class name: octree
# Sprite prototype(s): octree
# Creation mode: upfront
# 1. The number of octrees in a given world may be set using either
# global defaults or per-world values. The relevant parameters are:
#
# octree_minnum = Minimum number of octrees
# octree_maxnum = Maximum number of octrees
# For an explanation of how parameters such as these are set (globally
# or per world), see the preceding sections.
# 2. A player can hide behind an octree, but only once per octree.
# Leaving the octree or firing a weapon breaks cover.
# Assume:
#
# # $lv = Current world name
# # $octree_id = Sprite ID for an octree in world $lv
# # $ocplayer_id = Sprite ID for an ocplayer in world $lv
#
# If the following variable exists, it contains the sprite ID for an
# octree that the given ocplayer is presently hiding behind:
#
# gdata($lv,$ocplayer_id.octreehide_id)
#
# If the following variable exists, the given ocplayer has used (or is
# using) the given octree for hiding:
#
# gdata($lv,$octree_id,$ocplayer_id.octreehide_flag)
#---------------------------------------------------------------------
# Documentation: General tips.
# Destroying an object: If instances of a given object class can be
# destroyed by an ocbullet or other means, make calls similar to the
# following in the code where this occurs:
#
# set objclass .. ; # Set to object-class name
# set id ... ; # Set to sprite I.D.
# destroy_sprite $objclass $id
# Collisions: If a "run_" routine checks for collisions between ob-
# jects, and does something when a collision occurs, but doesn't de-
# stroy or move either of the objects involved in a collision, a col-
# lision lock may be required. The collision lock will be a variable
# used to prevent repeated processing of the same collision. For exam-
# ples of possible approaches, see the source code for "run_octree"
# and/or "run_occow".
#---------------------------------------------------------------------
# Documentation: bxdiv data format.
# This program supports (and uses) a simple data compression format
# that we'll call "bxdiv".
# A "bxdiv" data block consists of a sequence of 8-bit bytes. The
# block consists of a header followed by a segment that contains
# compressed data.
# The header starts with "bxdiv" followed by six decimal digits, which
# specify the format revision. Presently, the only supported format
# revision is "101009".
# For format "101009", the header includes one additional byte. The
# byte contains an integer from zero to 255. This is a "divisor". If
# the divisor is zero or one, the data represented is equal to the
# data segment; i.e., there is no translation. If the divisor is
# greater than one, the data represented may be decoded as follows:
# Take each byte in the data segment and replace it with N copies of
# the same byte, where N is the divisor.
#---------------------------------------------------------------------
# Documentation: Raw-sound operations: Simple hex-data version.
# If you're got a copy of Linux that has "sox", you can prepare and
# use raw-sound data as follows:
#
# (a) Start with a WAV file. We'll assume that the WAV-file name
# is "foo.wav".
#
# (b) Execute a Linux command similar to this:
#
# sox -V foo.wav -t ub -r 44100 -c 1 foo.ub
#
# If the volume needs to be adjusted, add a switch similar to
# -v 0.5 (lower-case "v") after "-V". Use lower numbers for
# lower volume and higher numbers for higher volume.
#
# (c) Produce a hex dump of "foo.ub" (with exactly two hex char-
# acters per byte). To do so under Linux, use commands similar
# to this:
#
# hexdump -v -e '34/1 "%02x"' -e '"\n"' foo.ub > foo.hex
#
# (d) To load the hex dump into a Tcl variable, use Tcl code simi-
# lar to this:
#
# set foo_hex ""
# append foo_hex \
# 67707f7a7677787f7a82887d7f7b787a77 \
# 86827a848a8c8b8d8a888381878a8b8083 \
# 67707f7a7677787f7
#
# (e) To prepare a Brick Engine-level version of the original
# sound, use Tcl code similar to this:
#
# set foo_bin [binary format H* $foo_hex]
# set foo_sound [br::sound load-raw $foo_bin]
# unset foo_bin foo_hex
#
# (f) To play the sound, use Tcl code similar to this:
#
# br::sound play $foo_sound
# Note: Even if you run "sox" under Linux, the output should work un-
# der both Windows and Linux.
#---------------------------------------------------------------------
# Documentation: Raw-sound operations: base64-data version.
# If you'd like to reduce the size of inline sound data, you can sub-
# stitute the following procedure for the simple procedure described
# in the previous section:
#
# (a) Start with a WAV file. We'll assume that the WAV-file name
# is "foo.wav".
#
# (b) Execute a Linux command similar to this:
#
# sox -V foo.wav -t ub -r 44100 -c 1 foo.ub
#
# If the volume needs to be adjusted, add a switch similar to
# -v 0.5 (lower-case "v") after "-V". Use lower numbers for
# lower volume and higher numbers for higher volume.
#
# (c) Produce a "base64" dump of "foo.ub". To do so under Linux,
# use a command similar to this:
#
# base64 --wrap=68 < foo.ub > foo.base64
#
# (d) To load the "base64" dump into a Tcl variable, use Tcl code
# similar to this:
#
# set foo_base64 ""
# append foo_base64 \
# gH2Bh3x/lpSEkoJ+k4aHhoqFdnd9jnlvfn \
# t5fn1ycX6FdGdwf3p2d3h/eoKIfX97eHp3 \
# hoJ6hIqMi42KiIOBh==
#
# (e) To prepare a Brick Engine-level version of the original
# sound, use Tcl code similar to this:
#
# set foo_bin [base64_decode $foo_base64]
# set foo_sound [br::sound load-raw $foo_bin]
# unset foo_bin foo_base64
#
# The routine called here (base64_decode) is included in this
# program.
#
# (f) To play the sound, use Tcl code similar to this:
#
# br::sound play $foo_sound
# Note: Even if you run "sox" under Linux, the output should work un-
# der both Windows and Linux.
#---------------------------------------------------------------------
# Documentation: Raw-sound operations: LZ77-base64 version.
# If you'd like to reduce the size of inline sound data further, start
# with the "base64" procedure described in the previous section and
# modify it as follows:
#
# Before producing the "base64" dump discussed in step (c), LZ77-com-
# press the data. To do so, use the "lzbetool" program mentioned pre-
# viously.
#
# Additionally, in step (e), replace:
#
# set foo_bin [base64_decode $foo_base64]
# with:
# set foo_bin [lz77_decode [base64_decode $foo_base64]]
# or:
# set foo_bin [lz77_base64_decode $foo_base64]
# The routines called here are included in this program.
#---------------------------------------------------------------------
# Documentation: Raw-sound operations: bxdiv-LZ77-base64 version.
# To reduce the size of inline sound data yet further, start with the
# with the "LZ77-base64" procedure described in the previous section
# and modify it as follows. Important: This should only be done for
# sound effects that can tolerate highly-lossy compression.
# Before LZ77-compressing the data, bxdiv-compress it. Use a divisor
# setting of somewhere from 2 to 10. Lower divisors will result in
# better quality. Higher divisors will result in better compression.
# To bxdiv-compress data, use the 'C' program "data2bxdiv.c", which
# should be available from the same place as this program.
# Additionally, in step (e), replace the "set foo_bin ..." statement
# with:
# set foo_bin [bxdiv_lz77_base64_decode $foo_base64]
# The routine called here is included in this program.
#---------------------------------------------------------------------
# Program parameters: Brick API level.
# For Brick 5.2, set BRICKAPI to 5200. For Brick 5.3, use 5300. For
# Brick 5.4 or above, use 5400. Important: Older APIs are supported
# only for testing purposes. If BRICKAPI is set to less than 5400,
# some features will be omitted and others will be only partly sup-
# ported.
set BRICKAPI 5400
#---------------------------------------------------------------------
# Documentation: Global variables.
# This documentation section is under construction.
# Important global variables include:
#
# gdata = Array: General global data
# layers = Array: To be documented
# sdata = Array: Sprite information related to the current world
# lv = Scalar: Current world name
#---------------------------------------------------------------------
# Program parameters: Basic colors.
set BLUE "0000FF"
set DARKGREEN "00AA00"
set DARKORANGE "8B4500"
set DARKWOOD "855E42"
set GREEN "00FF00"
set RED "FF0000"
set TOPAZ "0198E1"
set WHITE "FFFFFF"
set YELLOW "CCCC00"
#---------------------------------------------------------------------
# Program parameters: Dimensions and graphics.
# GAME_WIDTH and GAME_HEIGHT specify the width and height of the game
# display in pixels. These parameters should be set to 320 and 240,
# respectively.
set GAME_WIDTH 320
set GAME_HEIGHT 240
# To use OpenGL, set UseOpenGL to 1. To disable this mode, set this
# parameter to 0. For Brick 5.4 or above, the recommended setting is 0.
# For older Bricks, the recommended setting is 1.
# If OpenGL is used, the game won't work well with some graphics chip-
# sets. However, for Bricks before 5.4, the alternate mode (OpenGL dis-
# abled) may also exhibit problems. Ideally, Brick 5.4 or above should
# be used and OpenGL should be disabled. Note that this mode requires
# that a particular "cmake" option be used when Brick is built. For
# more information, see this program's changelog.
if { $BRICKAPI < 5400 } {
set UseOpenGL 1
} else {
set UseOpenGL 0
}
# DisplayWidth and DisplayHeight specify the display width and height
# parameters passed to "br::display open". The recommended settings
# are $GAME_WIDTH and $GAME_HEIGHT, respectively, except for Bricks
# older than 5.4 running without OpenGL. In the latter case, these
# settings should be doubled.
# DisplayScale specifies a scale factor (ignored for Bricks older
# than 5.4 running without OpenGL). This parameter should be a small
# positive integer. The recommended setting is 1 for OpenGL mode and
# 2 or 3 for non-OpenGL mode.
# FullScreen specifies a full-screen mode flag (may be "on" or "off").
# The recommended setting is "off".
if { $UseOpenGL } {
set DisplayWidth $GAME_WIDTH
set DisplayHeight $GAME_HEIGHT
set DisplayScale 1
set FullScreen off
} else {
if { $BRICKAPI < 5400 } {
set DisplayWidth [expr $GAME_WIDTH * 2]
set DisplayHeight [expr $GAME_HEIGHT * 2]
} else {
set DisplayWidth $GAME_WIDTH
set DisplayHeight $GAME_HEIGHT
}
set DisplayScale 3
set FullScreen off
}
#---------------------------------------------------------------------
# Program parameters: Music.
# If you'd like to play background music, set PlayMusic to 1; other-
# wise, 0. The factory setting is 1.
# Background music is built into the program; there's no need for an
# external music file.
# If you'd like to use the built-in music, set MusicFile to internal
# (and set PlayMusic to 1 as well). If you'd like to use an external
# music file instead, set MusicFile to a name (or pathname) for the
# file (and, again, set PlayMusic to 1). Note: In the latter case, any
# music-file type supported by the Brick Engine may be used.
# If you'd like to set music volume, set MusicVolume to a positive in-
# teger from one to 100. Otherwise, set this parameter to a negative
# integer, The factory setting is 70.
set PlayMusic 1
set MusicFile internal
set MusicVolume 70
#---------------------------------------------------------------------
# Program parameters: Frame and color formats.
# FRAFMTRGB and FRAFMTTRA specify Brick Engine frame-type strings.
# FRAFMTRGB may be used to create frames that contain only opaque pix-
# els and FRAFMTTRA may be used to create frames that may (or may not)
# contain non-opaque pixels.
# For Brick 5.2 or 5.3, both of the "FRAFMT..." parameters should be
# set to "rgb". For Brick 5.4, FRAFMTRGB should be set to "rgb" and
# FRAFMTTRA should be set to "rgba".
# For Brick 5.2 or 5.3, frame color values are six hex digits. For
# Brick 5.4, frames of type $FRAFMTRGB work the same way but frames of
# type $FRAFMTTRA use an eight-hex digit format: a six-hex digit RGB
# value followed by a two-hex digit opacity value.
# Large frames should be created using the $FRAFMTRGB type (and the
# associated color format) if possible. This produces faster programs
# in Brick 5.4.
# NRDIGITS specifies the number of hex digits in a FRAFMTRGB color
# value (always 6).
# NCDIGITS specifies the number of hex digits in a FRAFMTTRA color
# value (6 for Brick 5.2 or 5.3 and 8 for Brick 5.4).
if { $BRICKAPI < 5400 } {
set FRAFMTRGB rgb
set FRAFMTTRA rgb
set NCDIGITS 6
set NRDIGITS 6
} else {
set FRAFMTRGB rgb
set FRAFMTTRA rgba
set NCDIGITS 8
set NRDIGITS 6
}
#---------------------------------------------------------------------
# Program parameters: Transparency.
# TRANSPARRGB specifies a six-hex digit RGB value that will be mapped
# to transparency when six-hex digit RGB values in general are con-
# verted to Brick Engine frame color values. The color associated with
# TRANSPARRGB should chosen so that it's unlikely to conflict with
# commonly-used colors. The factory setting is FF00FF.
# TRANSPARFRA specifies a frame color value that will be treated as
# transparent. For Brick 5.2 or 5.3, this should be equal to $TRANS-
# PARRGB. For Brick 5.4, this should be equal to $TRANSPARRGB follow-
# ed by the hex digits 00.
# For Brick 5.2 or 5.3, the following three "CHROMA_..." parameters
# should be set:
#
# CHROMA_R Red component of $TRANSPARRGB in decimal (0 to 255)
# CHROMA_G Green component of $TRANSPARRGB in decimal (0 to 255)
# CHROMA_B Blue component of $TRANSPARRGB in decimal (0 to 255)
set TRANSPARRGB FF00FF
set TRANSPARFRA ${TRANSPARRGB}00
if { $BRICKAPI < 5400 } {
set CHROMA_R 255
set CHROMA_G 0
set CHROMA_B 255
set TRANSPARFRA $TRANSPARRGB
}
#---------------------------------------------------------------------
# Program parameters: Object background colors.
# Future change: These parameters should eventually be moved to the
# class level.
# Name Specifies background color for
# ------------------- ------------------------------
# BG_KARKINOS Karkinos
# BG_MEDICAL Medical kit
# BG_PLAYER Player
# BG_PORTAL_FORWARD Inter-world portal forward
# BG_PORTAL_INTRA Intra-world portal
# BG_PORTAL_REVERSE Inter-world portal reverse
# BG_SCROLL Scroll
set BG_KARKINOS $TRANSPARRGB
set BG_MEDICAL $TRANSPARRGB
set BG_PLAYER $TRANSPARRGB
set BG_PORTAL_FORWARD $YELLOW
set BG_PORTAL_INTRA $TRANSPARRGB
set BG_PORTAL_REVERSE $YELLOW
set BG_SCROLL $TRANSPARRGB
#---------------------------------------------------------------------
# Program parameters: Object foreground colors.
# Future change: These parameters should eventually be moved to the
# class level.
# Name Specifies foreground color for
# ------------------- ------------------------------
# FG_KARKINOS Karkinos
# FG_MEDICAL Medical kit
# FG_PLAYER Player
# FG_PORTAL_FORWARD Inter-world portal forward
# FG_PORTAL_INTRA Intra-world portal
# FG_PORTAL_REVERSE Inter-world portal reverse
# FG_SCROLL Scroll
set FG_KARKINOS $RED
set FG_MEDICAL $DARKGREEN
set FG_PLAYER $BLUE
set FG_PORTAL_FORWARD $DARKORANGE
set FG_PORTAL_INTRA $TOPAZ
set FG_PORTAL_REVERSE $DARKORANGE
set FG_SCROLL $DARKWOOD
#---------------------------------------------------------------------
# Program parameters: Background tiles and layer.
# BGTileWidth specifies the width of a background and/or info-display
# tile, in pixels. BGTileHeight is similar, but specifies height. The
# factory settings for these two parameters are 8 and 8, respective-
# ly.
# For the time being, BGTileWidth and BGTileHeight shouldn't be chang-
# ed. One or more routines (including "setup_background") still as-
# sume that the factory settings for the BGTile* parameters are used.
# If the settings are changed, the routines in question will need to
# be modified.
# BGWidth specifies the width of the background and/or info-display
# layer, in tiles. BGHeight is similar, but specifies height. Ideally,
# BGTileWidth times BGWidth should be equal to or a divisor of the
# "DisplayWidth" settings. Additionally, BGTileHeight times BGHeight
# should be equal to or a divisor of the "DisplayHeight" settings.
# The factory settings for BGTileWidth and BGTileHeight are 40 and 30,
# respectively.
set BGTileWidth 8
set BGTileHeight 8
set BGWidth 40
set BGHeight 30
#---------------------------------------------------------------------
# Program parameters: Random-map mode.
# To enable random maps, set RandomMapEnable to 1. To disable them,
# specify 0 instead. The factory setting is 1.
# Note: For special-case worlds, predefined maps may be used regard-
# less of the RandomMapEnable setting.
set RandomMapEnable 1
#---------------------------------------------------------------------
# Program parameters: Random-map dimensions.
# There are four random-map dimension parameters:
#
# Name Factory Specifies
# Setting
# ------------------ ------- -----------------------------
# RandomMapWidthMin 30 Minimum map width (in cells)
# RandomMapWidthMax 55 Maximum map width (in cells)
# RandomMapHeightMin 25 Minimum map height (in cells)
# RandomMapHeightMax 40 Maximum map height (in cells)
set RandomMapWidthMin 30
set RandomMapWidthMax 55
set RandomMapHeightMin 25
set RandomMapHeightMax 40
#---------------------------------------------------------------------
# Program parameters: Random-map tuning values.
# The following four parameters are tuning values used during random-
# map generation.
# RandomMapFollow should be an integer from 1 to 500. The factory set-
# ting is 150.
# RandomMapPoints should be an integer or real number from 0 to 150.
# The factory setting is 75.
# RandomMapMinSep1 and RandomMapMinSep2 should be integers from 4 to
# 10. The factory settings are 5 and 4, respectively.
set RandomMapFollow 150
set RandomMapPoints 75
set RandomMapMinSep1 5
set RandomMapMinSep2 4
#---------------------------------------------------------------------
# Program parameters: World names.
# Every world needs a unique name. World names are used as array keys,
# so they need to be spelled exactly the same way everywhere that
# they're used. Therefore, they're defined here as "gdata(World...)"
# entries. This approach makes it possible for Tcl to detect misspell-
# ings.
# World names should be short (they need to fit on the game's info
# display). They may use most printable characters and spaces, though
# not spaces at the beginning or end or multiple consecutive spaces.
# Dollar signs and double quotes are prohibited.
set gdata(WorldMain) "Qlaviql"
set gdata(WorldCaspak) "Caspak"
set gdata(WorldElysian) "Elysian Fields"
set gdata(WorldEndOfAllSongs) "End of All Songs"
set gdata(WorldEternia) "Eternia"
set gdata(WorldHeaven) "Heaven"
set gdata(WorldLimbo) "Limbo"
set gdata(WorldMilk) "Milk and Honey"
#---------------------------------------------------------------------
# Program parameters: Keyboard definitions.
# Each "Key..._Input" parameter should specify a Brick input-channel
# number (0 through 7). These parameters don't need to be unique.
# Each "Key..._Button" parameter should specify a Brick button number
# that's unique (and unused) for the associated input channel.
# Each "Key..._SDLCode" parameter should specify a standard "SDLK_..."
# keycode number.
# This program directs presses of a given key to the associated Brick
# input-channel and button combination.
set KeyH_Input 1 ; # h -key channel
set KeyH_Button 17 ; # h -key button
set KeyH_SDLCode 104 ; # h -key SDL code
set KeyI_Input 1 ; # i -key channel
set KeyI_Button 18 ; # i -key button
set KeyI_SDLCode 105 ; # i -key SDL code
set KeyQ_Input 0 ; # q -key channel
set KeyQ_Button 19 ; # q -key button
set KeyQ_SDLCode 113 ; # q -key SDL code
set KeySpace_Input 0 ; # space -key channel
set KeySpace_Button 18 ; # space -key button
set KeySpace_SDLCode 32 ; # space -key SDL code
#---------------------------------------------------------------------
# Program parameters: Misc.
# Normally, DebugLevel should be set to 0. If this parameter is set to
# a positive integer, the program will produce debugging output. Lar-
# ger settings will generally result in more output.
# FPS specifies the frame rate that the program aims for. This parame-
# ter is expressed in frames per second. The factory setting is 50.
# Note: If you change this, you may need to modify "FPS divisor" set-
# tings elsewhere.
set DebugLevel 0
set FPS 50
# InfoDisplayFieldWidth is a field width that's used to construct
# idsfmt, as shown here. The factory setting is 36. idsfmt is a format
# string that's used for lines in the program's info display.
set InfoDisplayFieldWidth 36
set idsfmt "s"
set idsfmt " %-$InfoDisplayFieldWidth$idsfmt"
# MaxGodTime specifies the maximum amount of time (in seconds) that
# the player spends in "God" mode after receiving a "God" power-up.
# The factory setting is 120.
# NameAndRevision specifies a short string that identifies the program
# name and revision. This string should be less than 21 characters
# long. The factory settings is "BEWorld" plus a space and a six-digit
# revision number.
set MaxGodTime 120
set NameAndRevision "BEWorld 110923"
# WorldKeyStart is used to construct "gdata" keys that include data
# related to various worlds. Any reasonably unique text string should
# work. The factory setting is "WORLD_PARAM". For more information,
# see any world-definition section.
set WorldKeyStart "WORLD_PARAM"
#---------------------------------------------------------------------
# Routine: dmproc
# Purpose: Extended version of "dmproc"
# Arguments: (special case, see below)
#---------------------------------------------------------------------
# "dmproc" is an extended version of "proc" that adds some debugging
# features.
# "dmproc" takes the same arguments as "proc", with one addition:
# There's a new first argument named "msglev", which must be an inte-
# ger.
# "dmproc" assumes that a global variable named DebugLevel is defined
# and contains an integer.
# If $DebugLevel is greater than or equal to one at compile time,
# "dmproc" prints a single-line message of the form "define NAME",
# where NAME is the name of the routine that's being defined.
# "dmproc" also makes some changes to the routine in question:
#
# (a) Define a local variable named rtn that contains the name of
# the routine.
#
# (b) If $DebugLevel is greater than or equal to $msglev when the
# routine is called, print the name of the routine.
#
# (c) Define a local variable named IE that contains an internal-
# error message prefix string. The string includes the name of
# the routine.
#---------------------------------------------------------------------
proc dmproc { msglev pname arglist body } {
global DebugLevel
if { $DebugLevel >= 1 } { puts "define $pname" }
set newcode ""
append newcode "set rtn $pname\n";
append newcode "global DebugLevel\n";
append newcode {if { $DebugLevel >= MSGLEV } { puts $rtn }}
append newcode "\n"
append newcode {set IE "$rtn: panic"}
append newcode "\n"
regsub -all {MSGLEV} $newcode $msglev newcode
proc $pname $arglist "$newcode$body"
}
#---------------------------------------------------------------------
# Routine: xproc
# Purpose: Extended version of "proc"
# Arguments: (special case, see below)
# "xproc" is an extended version of "proc" that supports pass-by-ref-
# erence. You can use it the same way as "proc", but any argument
# names that are prefixed with "&" are automatically passed by refer-
# ence.
# This routine is apparently by one or more of the following: Keith
# Vetter, Donal Fellows, Andreas Leitgeb. It's believed to be redis-
# tributable.
# Original web-page URL (valid as of 2010):
#
# http://wiki.tcl.tk/4535
#---------------------------------------------------------------------
proc xproc { pname arglist body } {
set newcode ""
foreach arg $arglist {
set arg [lindex $arg 0]
if { [string match "&*" $arg] } {
set bare [string range $arg 1 end]
append newcode \
"upvar 1 \[set [list $arg]\] [list $bare]\n"
}}
proc $pname $arglist "$newcode#original body follows:\n$body"
}
#---------------------------------------------------------------------
# Routine: lrandom
# Purpose: Returns a random element from a list
# Arguments: xlist = A Tcl list
#---------------------------------------------------------------------
dmproc 3 lrandom { xlist } {
return [lindex $xlist [expr { int (rand() * [llength $xlist]) }]]
}
#---------------------------------------------------------------------
# Routine: set_class_defaults_barnyard
# Purpose: Set class defaults based on barnyard prototype
# Arguments: objclass = Object-class name
#---------------------------------------------------------------------
# This routine sets the class defaults for the specified class to val-
# ues that may be suitable for something similar to an occow. The cal-
# ler may change individual defaults subsequently to fine-tune the re-
# sults. Additionally, defaults may be overridden on a per-world bas-
# is.
# Presently, the following defaults are set (using the values shown
# here):
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# divmax Maximum speed divisor 5
# divmin Minimum speed divisor 4
# dropshadow Flag: Add drop shadow 1
# frequency Creation frequency 0.00500
# maxnum Maximum no. that can exist in one world 1
# minnum Minimum no. that can exist in one world 1
# preload Number of instances to preload 1
# scalemin Minimum scale factor (may be a real) 1.00
# scalemax Maximum scale factor (may be a real) 2.00
# shoot_can Flag: Can shoot one of these 1
# shoot_effect Effect on object's health per ocbullet -1
# shoot_score Score change if destroyed by shooting 0
# smartmax Maximum percentage that are smart 95
# smartmin Minimum percentage that are smart 95
# zhint Render-order hint 2
#---------------------------------------------------------------------
dmproc 3 set_class_defaults_barnyard { objclass } {
global gdata
set gdata(Default_${objclass}_divmax) 5
set gdata(Default_${objclass}_divmin) 4
set gdata(Default_${objclass}_dropshadow) 1
set gdata(Default_${objclass}_frequency) 0.00500
set gdata(Default_${objclass}_maxnum) 1
set gdata(Default_${objclass}_minnum) 1
set gdata(Default_${objclass}_preload) 1
set gdata(Default_${objclass}_scalemin) 1.00
set gdata(Default_${objclass}_scalemax) 2.00
set gdata(Default_${objclass}_shoot_can) 1
set gdata(Default_${objclass}_shoot_effect) -1
set gdata(Default_${objclass}_shoot_score) 0
set gdata(Default_${objclass}_smartmax) 95
set gdata(Default_${objclass}_smartmin) 95
set gdata(Default_${objclass}_zhint) 2
}
#---------------------------------------------------------------------
# Class parameters: Defaults for ocbullet class.
# Defaults may be overridden on a per-world basis.
# Parameter Purpose Factory value
# ---------- --------------------------------------- -------------
# divmax Maximum speed divisor 1
# divmin Minimum speed divisor 1
# maxnum Maximum no. that can exist in one world 20
# minnum Minimum no. that can exist in one world 0
set gdata(Default_ocbullet_divmax) 1
set gdata(Default_ocbullet_divmin) 1
set gdata(Default_ocbullet_maxnum) 20
set gdata(Default_ocbullet_minnum) 0
#---------------------------------------------------------------------
# Class parameters: Defaults for occar class.
# This class starts with the "barnyard" prototype discussed previous-
# ly and makes some adjustments. Defaults may be overridden on a per-
# world basis.
set_class_defaults_barnyard occar
set gdata(Default_occar_minnum) 0
set gdata(Default_occar_maxnum) 0
set gdata(Default_occar_preload) 0
set gdata(Default_occar_scalemin) 1.00
set gdata(Default_occar_scalemax) 1.00
; # List of possible names
set gdata(Default_occar_name) [list \
Chitty-Chitty Herbie KARR KITT
]
#---------------------------------------------------------------------
# Class parameters: Defaults for occow class.
# This class starts with the "barnyard" prototype discussed previously
# and makes some adjustments. Defaults may be overridden on a per-
# world basis.
set_class_defaults_barnyard occow
set gdata(Default_occow_scalemin) 2.00
set gdata(Default_occow_scalemax) 2.00
; # List of possible names
set gdata(Default_occow_name) [list \
Bessie Buttercup Clarabelle Daisy Kalikau Latavao \
Palauni Tangaloa \
]
#---------------------------------------------------------------------
# Class parameters: Defaults for occross class.
# This class starts with the "barnyard" prototype discussed previously
# and makes some adjustments. Defaults may be overridden on a per-
# world basis.
set_class_defaults_barnyard occross
set gdata(Default_occross_frequency) 0
set gdata(Default_occross_maxnum) 0
set gdata(Default_occross_minnum) 0
set gdata(Default_occross_preload) 0
set gdata(Default_occross_scalemin) 2
set gdata(Default_occross_scalemax) 2
set gdata(Default_occross_shoot_can) 0
#---------------------------------------------------------------------
# Class parameters: Defaults for ocdog class.
# This class starts with the "barnyard" prototype discussed previous-
# ly and makes some adjustments. Defaults may be overridden on a per-
# world basis.
set_class_defaults_barnyard ocdog
set gdata(Default_ocdog_scalemax) 1.00
; # List of possible names
set gdata(Default_ocdog_name) [list \
Cerebus Lassie Rin-Tin-Tin Rover Spot
]
#---------------------------------------------------------------------
# Class parameters: Defaults for ocflames class.
# "ocflames" is a special case. This class is only instantiated for
# one world, only once there, it's placed at a fixed location, and it
# behaves more like an environment than an object (so, for example,
# "nobounce" needs to be non-zero).
# Defaults may be overridden on a per-world basis.
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# dropshadow Flag: Add drop shadow 0
# forceposn Flag: Place at a specified position 1
# frequency Creation frequency 0
# heffect Effect on player's health per attack -1
# maxnum Maximum no. that can exist in one world 0
# minnum Minimum no. that can exist in one world 0
# nobounce Flag: Suppress positioning bounces 1
# preload Number of instances to preload 0
# scalemin Minimum scale factor (may be a real) 3.00
# scalemax Maximum scale factor (may be a real) 3.00
# shoot_can Flag: Can shoot one of these 0
# xpos X-position (in pixels) 8
# ypos Y-position (in pixels) 8
# zhint Render-order hint 0
set gdata(Default_ocflames_dropshadow) 0
set gdata(Default_ocflames_forceposn) 1
set gdata(Default_ocflames_frequency) 0
set gdata(Default_ocflames_heffect) -1
set gdata(Default_ocflames_maxnum) 0
set gdata(Default_ocflames_minnum) 0
set gdata(Default_ocflames_nobounce) 1
set gdata(Default_ocflames_preload) 0
set gdata(Default_ocflames_scalemin) 3.0
set gdata(Default_ocflames_scalemax) 3.0
set gdata(Default_ocflames_shoot_can) 0
set gdata(Default_ocflames_xpos) 8
set gdata(Default_ocflames_ypos) 8
set gdata(Default_ocflames_zhint) 0
#---------------------------------------------------------------------
# Class parameters: Defaults for ocinter class.
# Presently, there are none.
#---------------------------------------------------------------------
# Class parameters: Defaults for ocintra class.
# Presently, there are none.
#---------------------------------------------------------------------
# Class parameters: Defaults for ockarkinos class.
# Defaults may be overridden on a per-world basis.
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# divmax Maximum speed divisor 5
# divmin Minimum speed divisor 2
# dropshadow Flag: Add drop shadow 1
# frequency Creation frequency 0.00400
# heffect Effect on player's health per attack -1
# maxnum Maximum no. that can exist in one world 20
# minnum Minimum no. that can exist in one world 0
# preload Number of instances to preload 0
# shoot_can Flag: Can shoot one of these 1
# shoot_effect Effect on object's health per ocbullet -1
# shoot_score Score change if destroyed by shooting 1
# smartmax Maximum percentage that are smart 95
# smartmin Minimum percentage that are smart 95
# zhint Render-order hint 1
# name List of possible names (various)
set gdata(Default_ockarkinos_divmax) 5
set gdata(Default_ockarkinos_divmin) 2
set gdata(Default_ockarkinos_dropshadow) 1
set gdata(Default_ockarkinos_frequency) 0.00400
set gdata(Default_ockarkinos_heffect) -1
set gdata(Default_ockarkinos_maxnum) 20
set gdata(Default_ockarkinos_minnum) 0
set gdata(Default_ockarkinos_preload) 0
set gdata(Default_ockarkinos_shoot_can) 1
set gdata(Default_ockarkinos_shoot_effect) -1
set gdata(Default_ockarkinos_shoot_score) 1
set gdata(Default_ockarkinos_smartmax) 95
set gdata(Default_ockarkinos_smartmin) 95
set gdata(Default_ockarkinos_zhint) 1
# Note: Names ending with "*" are female. The "*" isn't displayed at
# runtime.
; # List of possible names
set gdata(Default_ockarkinos_name) [list \
Adjur Aghi Akten Ankhisk Anog Antaan \
Aperakei Argan Arizhel* AsKade Atro Auloh \
Azetbor* Badich Ba'el* Batahr Batrell Be'Elanna* \
B'Etor* B'iJik Chang ChaqI D'cIq Dezhe \
D'Ghor Divok Dracla Drex DuKath Dula \
Durall Duras Edronh Eragh Gelly* Gistad \
Godar Goradh Gorkon Gowron Graade Grilka* \
Gudag G'Vera* Halaylah hiJak Hon'Tihl Huraga \
Inagh Janar Ja'rod J'Ddan Kaden Kadi \
Kadrya* Kaftter Kagga Kahless Kahlest* KaiTan \
Kalan Kalim Kalin Kandel Kang Kanjis \
Karden Kargan Katilla* Kaybok K'Ehleyr* Kelay \
Kell Kellein* Kellen Kelly* Keppa Keroth \
Kessec Kessum Kethas Kev KezhKe Khidri \
Kian Kintata Klaa Klag Kle'eg Klimor \
K'mpek K'mpok K'mtar K'nara Kodan Koll \
Koloth Komakh Konmel Konora* Koord Koplo \
Kor Koronin* Koroth Korrath Korris K'Orta \
Koth KothKe Koval Kowla Kozak Kras \
K'Ratak K'rau Kreg Krenn K'Rodak Kromm \
Kruge Kruger k'taH* K'tal K'Tar* K'Tel \
K'Temok K'Tesh Kulan Kulge Kurak* Kurn \
Kurrozh K'Vada Larg Largh Ler'at L'Kor \
Lursa* Mabli Maglus Maida Majjas Makai \
Maltz Manda* Mara* Margon Martok Memeth \
Merzhan Mogh Mohtr Molor Morag Morath \
M'Rel Muuda Najuk Nedec Noj Nu'Daq \
olahg olmai Ondagh Pok Porus Qua'lon \
Qugh Ragga Rajuc Rannuf Restagh Rocta \
RoKis Ruzhe Seegath Seeth Segon Shurin \
Starad Surgh SvaD Tagre Tellot* Tel'Peh \
Tiehar Tignor T'lak T'lanak Tog Toragh \
Torak Toral Torghn Torin T'Rok Tumek \
T'Var* Unagroth U'Qam Vagh Valkris* Vathraq \
VeKma* Vixis* Vok Voloh Vrenn Yatron \
Zharn ZhoKa \
]
# Related notes:
# The program now supports both hunters and grazers as enemies. Gra-
# zers move at random. Hunters chase the player.
# For "ockarkinos", "smartmin" and "smartmax" specify the minimum and
# maximum percentages of the "ockarkinos" population (in a given
# world) that are hunters.
# Hunters aren't intelligent. In particular, they don't know know to
# get around walls. As a kludge, if a hunter hits a wall, it changes
# into a grazer temporarily. This increases the chances that the hunt-
# er will be able to find a new route.
# If you're using Brick 5.2, the "hunters" feature requires the 100922
# "bricktcl" patch to "wrap_sprite_position". If the patch is missing,
# the program will crash. This isn't an issue for newer releases of
# Brick.
#---------------------------------------------------------------------
# Class parameters: Defaults for ocmedical class.
# Defaults may be overridden on a per-world basis.
# Parameter Purpose Factory value
# ---------- --------------------------------------- -------------
# cautious Become cautious if player is this close 50
# dropshadow Flag: Add drop shadow 1
# frequency Creation frequency 0.00200
# health See remarks below 15
# maxnum Maximum no. that can exist in one world 2
# minnum Minimum no. that can exist in one world 4
# preload Number of instances to preload 1
# shoot_can Flag: Can shoot one of these 1
# shoot_effect Effect on object's health per ocbullet -1
# shoot_score Score change if destroyed by shooting 0
# zhint Render-order hint 1
# name List of possible names (various)
set gdata(Default_ocmedical_cautious) 50
set gdata(Default_ocmedical_dropshadow) 1
set gdata(Default_ocmedical_frequency) 0.00200
set gdata(Default_ocmedical_health) 15
set gdata(Default_ocmedical_maxnum) 2
set gdata(Default_ocmedical_minnum) 4
set gdata(Default_ocmedical_preload) 1
set gdata(Default_ocmedical_shoot_can) 1
set gdata(Default_ocmedical_shoot_effect) -1
set gdata(Default_ocmedical_shoot_score) 0
set gdata(Default_ocmedical_zhint) 1
; # List of possible names
set gdata(Default_ocmedical_name) [list \
Chidori Chiyo Chizu Kado Kaemon \
Kagami Kamenosuke Katsutoshi Kazuo Keiji \
Keitaro Machi Makoto Maro Masahiro \
Nagisa Naoko Ogano Ozuru Raiden \
Renjiro Sachi Sakae Samaru Taizo \
Tani Taro Yasahiro Yoshi Yukiko \
Zinan
]
# Related notes:
# "health" specifies the number of health points that an ocmedical is
# worth. This should be a positive integer from 1 to 100. The fac-
# tory setting is 15.
#---------------------------------------------------------------------
# Class parameters: Defaults for ocmoney class.
# Defaults may be overridden on a per-world basis.
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# cautious Become cautious if player is this close 50
# divmax Maximum speed divisor 5
# divmin Minimum speed divisor 4
# dropshadow Flag: Add drop shadow 1
# maxnum Maximum no. that can exist in one world 3
# minnum Minimum no. that can exist in one world 1
# shoot_can Flag: Can shoot one of these 1
# shoot_effect Effect on object's health per ocbullet -1
# shoot_score Score change if destroyed by shooting 0
# smartmax Maximum percentage that are smart 95
# smartmin Minimum percentage that are smart 95
# valmax Maximum value per instance 25
# valmin Minimum value per instance 2
# name List of possible names (various)
set gdata(Default_ocmoney_cautious) 50
set gdata(Default_ocmoney_divmax) 5
set gdata(Default_ocmoney_divmin) 4
set gdata(Default_ocmoney_dropshadow) 1
set gdata(Default_ocmoney_maxnum) 3
set gdata(Default_ocmoney_minnum) 1
set gdata(Default_ocmoney_shoot_can) 1
set gdata(Default_ocmoney_shoot_effect) -1
set gdata(Default_ocmoney_shoot_score) 0
set gdata(Default_ocmoney_smartmax) 95
set gdata(Default_ocmoney_smartmin) 95
set gdata(Default_ocmoney_valmax) 25
set gdata(Default_ocmoney_valmin) 2
; # List of possible names
set gdata(Default_ocmoney_name) [list \
Bill Happiness Joy Success Truth
]
#---------------------------------------------------------------------
# Class parameters: Defaults for ocpig class.
# This class starts with the "barnyard" prototype discussed previous-
# ly and makes some adjustments. Defaults may be overridden on a per-
# world basis.
set_class_defaults_barnyard ocpig
set gdata(Default_ocpig_scalemax) 1.00
; # List of possible names
set gdata(Default_ocpig_name) [list \
Babe Bacon Barnaby Freddy Hamm Harold \
Peppermint Porkchop Wilbur
]
#---------------------------------------------------------------------
# Class parameters: Defaults for ocplayer class.
# Parameter Purpose Factory value
# ---------- --------------------------------------- -------------
# dropshadow Flag: Add drop shadow 1
set gdata(Default_ocplayer_dropshadow) 1
#---------------------------------------------------------------------
# Class parameters: Defaults for ocscroll class.
# Defaults may be overridden on a per-world basis.
# Parameter Purpose Factory value
# ---------- --------------------------------------- -------------
# dropshadow Flag: Add drop shadow 1
# maxnum Maximum no. that can exist in one world 2
# minnum Minimum no. that can exist in one world 1
# zhint Render-order hint 1
set gdata(Default_ocscroll_dropshadow) 1
set gdata(Default_ocscroll_maxnum) 2
set gdata(Default_ocscroll_minnum) 1
set gdata(Default_ocscroll_zhint) 1
#---------------------------------------------------------------------
# Class parameters: Defaults for octiger class.
# Defaults may be overridden on a per-world basis.
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# divmax Maximum speed divisor 5
# divmin Minimum speed divisor 2
# dropshadow Flag: Add drop shadow 1
# health Initial health points 7
# heffect Effect on player's health per attack -3
# maxnum Maximum no. that can exist in one world 2
# minnum Minimum no. that can exist in one world 2
# scalemin Minimum scale factor (may be a real) 1.00
# scalemax Maximum scale factor (may be a real) 1.00
# shoot_can Flag: Can shoot one of these 1
# shoot_effect Effect on object's health per ocbullet -1
# shoot_score Score change if destroyed by shooting 5
# sound_destroy Sound when destroyed briefmeow
# sound_hit Sound when hit briefmeow
# smartmax Maximum percentage that are smart 95
# smartmin Minimum percentage that are smart 95
# name List of possible names (various)
set gdata(Default_octiger_divmax) 5
set gdata(Default_octiger_divmin) 2
set gdata(Default_octiger_dropshadow) 1
set gdata(Default_octiger_health) 7
set gdata(Default_octiger_heffect) -3
set gdata(Default_octiger_maxnum) 2
set gdata(Default_octiger_minnum) 2
set gdata(Default_octiger_scalemin) 1.00
set gdata(Default_octiger_scalemax) 1.00
set gdata(Default_octiger_shoot_can) 1
set gdata(Default_octiger_shoot_effect) -1
set gdata(Default_octiger_shoot_score) 5
set gdata(Default_octiger_sound_destroy) briefmeow
set gdata(Default_octiger_sound_hit) briefmeow
set gdata(Default_octiger_smartmax) 95
set gdata(Default_octiger_smartmin) 95
; # List of possible names
set gdata(Default_octiger_name) [list \
Catrina Catzandra Catzilla Fluffy Magnificat Mewsette \
Raggles Ripley Tabby \
]
#---------------------------------------------------------------------
# Class parameters: Defaults for octree class.
# Defaults may be overridden on a per-world basis.
# Parameter Use Factory value
# ---------- --------------------------------------- -------------
# dropshadow Flag: Add drop shadow 1
# maxnum Maximum no. that can exist in one world 10
# minnum Minimum no. that can exist in one world 0
# smartmax Maximum percentage that are smart 95
# smartmin Minimum percentage that are smart 95
# octigerdelta See remarks below 20
# zhint Render-order hint 10
set gdata(Default_octree_dropshadow) 1
set gdata(Default_octree_maxnum) 10
set gdata(Default_octree_minnum) 0
set gdata(Default_octree_smartmax) 95
set gdata(Default_octree_smartmin) 95
set gdata(Default_octree_octigerdelta) 20
set gdata(Default_octree_zhint) 10
# Related notes:
# If a given octree (or forest) has a hidden octiger, and the X and Y
# distances from a given octree to an ocplayer are both less than the
# associated "octiger_delta" setting, the hidden octiger will be in-
# stantiated.
#---------------------------------------------------------------------
# "Bounce" list for "random_position_sprite".
# If "random_position_sprite" shouldn't drop objects on instances of a
# given class, or drop instances of that class on anything else, add
# the name of the class to the following list (list_classes_bounce).
# The list should include ocplayer, non-mobile classes, and classes
# whose instances are activated and/or destroyed when the player pass-
# es over them.
# For now, mobile classes other than ocplayer should be omitted except
# where it's important to keep instances of these classes from inter-
# acting with instances of other classes based on random positioning.
# Explanation: If classes are listed here unnecessarily, this may
# cause problems for "random_position_sprite".
# Note: If the "nobounce" flag is set elsewhere for a given class
# (call it foo for the sake of discussion), "random_position_sprite"
# may drop an object of any class (call it bar) on an instance of foo
# whether or not bar is listed here.
# It's important to set "nobounce" for classes whose objects have
# large dimensions. If this isn't done, this may also cause problems
# for "random_position_sprite".
set gdata(list_classes_bounce) \
[list ocinter ocintra ocplayer ocscroll octree]
#---------------------------------------------------------------------
# Other class-category lists.
# As explained elsewhere, some object classes are classified as
# "periodic". The associated class names should be added to the fol-
# lowing list:
set gdata(list_classes_periodic) \
[list occow occross ocdog ockarkinos ocpig ocmedical]
# As explained elsewhere, some object classes are classified as
# "upfront". The associated class names should be added to the follow-
# ing list:
set gdata(list_classes_upfront) \
[list occar ocflames ocintra ocscroll ocmoney octree]
# Note: A given class shouldn't be both "periodic" and "upfront". How-
# ever, this won't cause significant problems presently.
#---------------------------------------------------------------------
# List of classes with sprite prototypes.
# The list defined here holds the names of the classes for which
# sprite prototypes exist.
# The list should be initialized to empty here; names are added at a
# later point.
set gdata(list_classes_proto) [list]
#---------------------------------------------------------------------
# List of sounds.
# The list defined here holds the names of the sounds for which
# "_bxdiv_lz77_base64" data exists. "setup_sound_effects" converts the
# sounds in question to internal (playable) format.
# The list should be initialized to empty here; names are added at a
# later point.
# Note: In some contexts, this program prepends "sound_" to sound
# names. However, the "sound_" prefix is omitted in this context.
set gdata(list_sounds) [list]
#---------------------------------------------------------------------
# Compressed music file.
# music_lz77_base64 contains a LZ77-compressed and base64-encoded ver-
# sion of the program's internal music file; i.e., the music file
# that's played when PlayMusic is set to 1 and MusicFile is set to
# internal.
# Presently, the following music file is used: "heatbeat.mod". The MOD
# file in question is licensed under Creative Commons and is therefore
# redistributable. The artist is Aleksi Eeben.
set music_lz77_base64 ""
append music_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
DjsBCh8CABwOIg==
#---------------------------------------------------------------------
# Compressed "ocscroll" data.
# wisdom_lz77_base64 contains a LZ77-compressed and base64-encoded
# version of a "fortunes" file; i.e., a text file that contains quotes
# (or "fortunes"). The quotes are used by ocscrolls.
# For an explanation of the "fortunes"-file format, and more informa-
# tion about ocscrolls, see the documentation section named "Scrolls".
set wisdom_lz77_base64 ""
append wisdom_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AAsS2EV4Y2VwAgAGE4ggdG9sAgAHAlZQaGlsaXAgTW9lAgAIG6o=
#---------------------------------------------------------------------
# Compressed "bonus" sound.
lappend gdata(list_sounds) bonus
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set bonus_bxdiv_lz77_base64 ""
append bonus_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
BFE=
#---------------------------------------------------------------------
# Compressed "briefmeow" sound.
lappend gdata(list_sounds) briefmeow
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set briefmeow_bxdiv_lz77_base64 ""
append briefmeow_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AgAKBokBDIsCAAgEJQIACQJMAgAFAVF9AgAGG9UCAAYEPwIABQogAgAPAVIBB8d/
#---------------------------------------------------------------------
# Compressed "gunshot" sound.
lappend gdata(list_sounds) gunshot
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set gunshot_bxdiv_lz77_base64 ""
append gunshot_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
eAIAEQIlAgAPAT0BDGcCAAcBIQEHAQ==
#---------------------------------------------------------------------
# Compressed "hit" sound.
lappend gdata(list_sounds) hit
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set hit_bxdiv_lz77_base64 ""
append hit_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
CQL4ARcYAQsZAgANAygCAA0DZgEaPgEaYwIABQN3ASU4ASujARQYAQUGAQsLARE4
#---------------------------------------------------------------------
# Compressed "loser" sound.
lappend gdata(list_sounds) loser
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set loser_bxdiv_lz77_base64 ""
append loser_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
EQLeAgAGA4I=
#---------------------------------------------------------------------
# Compressed "occar" sound.
lappend gdata(list_sounds) occar
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set occar_bxdiv_lz77_base64 ""
append occar_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
GDQCADgBdgEUKgIACCCw
#---------------------------------------------------------------------
# Compressed "occow" sound.
lappend gdata(list_sounds) occow
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set occow_bxdiv_lz77_base64 ""
append occow_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
BdwBFFuB
#---------------------------------------------------------------------
# Compressed "occross" sound.
lappend gdata(list_sounds) occross
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set occross_bxdiv_lz77_base64 ""
append occross_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AgAFBuIBBZY=
#---------------------------------------------------------------------
# Compressed "ocdog" sound.
lappend gdata(list_sounds) ocdog
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set ocdog_bxdiv_lz77_base64 ""
append ocdog_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AAYU1QIADwLiAgAJBCMBEEQBD54=
#---------------------------------------------------------------------
# Compressed "ocflames" sound.
lappend gdata(list_sounds) ocflames
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set ocflames_bxdiv_lz77_base64 ""
append ocflames_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
BQGaAgAFG2MCAAcDBQEHCQEOAQIABQamAQVe
#---------------------------------------------------------------------
# Compressed "ocintra" sound.
lappend gdata(list_sounds) ocintra
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set ocintra_bxdiv_lz77_base64 ""
append ocintra_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AQgJAQ0KAQXgAQUBAQRMAQTgAQbeAQ0BAQYcARsSAQkPAQQJgg==
#---------------------------------------------------------------------
# Compressed "ocpig" sound.
lappend gdata(list_sounds) ocpig
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set ocpig_bxdiv_lz77_base64 ""
append ocpig_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
gYiQgn6Dgnd0end8iX9vfYuKg35/hYKDgoN/fICHhIGDhYGChgIAOwSKASw0
#---------------------------------------------------------------------
# Compressed "octiger" sound.
lappend gdata(list_sounds) octiger
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set octiger_bxdiv_lz77_base64 ""
append octiger_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
Gj+BgoKDAgAGI1wBBhsCAAUiZwIABRuBAgAFGoEBBGcBBRUBBzsCAAUcUgEHDg==
#---------------------------------------------------------------------
# Compressed "pop" sound.
lappend gdata(list_sounds) pop
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set pop_bxdiv_lz77_base64 ""
append pop_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
Bn0CAAoGCQEQFwIACQGgAgAIAVQBAwQ=
#---------------------------------------------------------------------
# Compressed "poweroff" sound.
lappend gdata(list_sounds) poweroff
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set poweroff_bxdiv_lz77_base64 ""
append poweroff_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
AQ+/AgAIFGACAApPEgIAChyrAgALAosBBh0=
#---------------------------------------------------------------------
# Compressed "win" sound.
lappend gdata(list_sounds) win
# This variable contains a sound effect compressed (and encoded) using
# the "bxdiv-LZ77-base64" procedure discussed in the program document-
# ation.
set win_bxdiv_lz77_base64 ""
append win_bxdiv_lz77_base64 \
# Binary data removed to make browsing easier. For a complete version,
# see the actual source code.
ChoBC2YBBroBCQEBDAwCAA042AIAFTja
#---------------------------------------------------------------------
# Misc. data.
# Future change: Document this data.
set fr1data ""
append fr1data \
AAAAAA AAAAAA AAAAAA AAAAAA CCCCCC CCCCCC CCCCCC CCCCCC \
AAAAAA AAAAAA AAAAAA CCCCCC CCCCCC CCCCCC CCCCCC AAAAAA \
AAAAAA AAAAAA CCCCCC CCCCCC CCCCCC CCCCCC AAAAAA AAAAAA \
AAAAAA CCCCCC CCCCCC CCCCCC CCCCCC AAAAAA AAAAAA AAAAAA \
CCCCCC CCCCCC CCCCCC CCCCCC AAAAAA AAAAAA AAAAAA AAAAAA \
CCCCCC CCCCCC CCCCCC AAAAAA AAAAAA AAAAAA AAAAAA CCCCCC \
CCCCCC CCCCCC AAAAAA AAAAAA AAAAAA AAAAAA CCCCCC CCCCCC \
CCCCCC AAAAAA AAAAAA AAAAAA AAAAAA CCCCCC CCCCCC CCCCCC
set fr2data ""
append fr2data \
DDDDDD DDDDDD DDDDDD DDDDDD CCCCCC CCCCCC CCCCCC CCCCCC \
DDDDDD DDDDDD DDDDDD CCCCCC CCCCCC CCCCCC CCCCCC DDDDDD \
DDDDDD DDDDDD CCCCCC CCCCCC CCCCCC CCCCCC DDDDDD DDDDDD \
DDDDDD CCCCCC CCCCCC CCCCCC CCCCCC DDDDDD DDDDDD DDDDDD \
CCCCCC CCCCCC CCCCCC CCCCCC DDDDDD DDDDDD DDDDDD DDDDDD \
CCCCCC CCCCCC CCCCCC DDDDDD DDDDDD DDDDDD DDDDDD CCCCCC \
CCCCCC CCCCCC DDDDDD DDDDDD DDDDDD DDDDDD CCCCCC CCCCCC \
CCCCCC DDDDDD DDDDDD DDDDDD DDDDDD CCCCCC CCCCCC CCCCCC
set fr3data ""
append fr3data \
CCCCCC CCCCCC CCCCCC AAAAAA CCCCCC CCCCCC CCCCCC AAAAAA \
CCCCCC 888888 888888 444444 CCCCCC 888888 888888 444444 \
CCCCCC 888888 888888 444444 CCCCCC 888888 888888 444444 \
AAAAAA 444444 444444 444444 AAAAAA 444444 444444 444444 \
CCCCCC CCCCCC CCCCCC AAAAAA CCCCCC CCCCCC CCCCCC AAAAAA \
CCCCCC 888888 888888 444444 CCCCCC 888888 888888 444444 \
CCCCCC 888888 888888 444444 CCCCCC 888888 888888 444444 \
AAAAAA 444444 444444 444444 AAAAAA 444444 444444 444444
#---------------------------------------------------------------------
# Routine: setup_graphics
# Purpose: Sets up graphics mode
# Arguments: None
# For related information, see the comments in the "program parame-
# ters" section named "Graphics".
#---------------------------------------------------------------------
dmproc 1 setup_graphics {} {
global BRICKAPI UseOpenGL
global DisplayWidth DisplayHeight DisplayScale FullScreen
if { $UseOpenGL > 0 } {
br::graphics open accel \
$DisplayWidth $DisplayHeight $FullScreen $DisplayScale
} else {
if { $BRICKAPI < 5400 } {
br::graphics open sdl \
$DisplayWidth $DisplayHeight $FullScreen
} else {
set gra_opts [list sdl]
if { $FullScreen } { lappend gra_opts fs }
br::graphics open \
$DisplayWidth $DisplayHeight $DisplayScale 0 $gra_opts
}
}
br::graphics window-title "OldCoder BEWorld"
}
#---------------------------------------------------------------------
# Routine: lz77_decode
# Purpose: Decompresses LZ77-compressed data
# Arguments: data = LZ77-compressed data
# This routine decompresses the input data and returns the result.
# Note: If you'd like to create compressed data that's compatible with
# this routine, you'll need to use a separate LZ77 compression tool
# named "lzbetool". "lzbetool" is a short pure-Tcl script that should
# be available from the same place as this program.
# This routine isn't original, but it's believed to be redistributa-
# ble. It's based on code by Miguel Sofer that was obtained from:
#
# http://wiki.tcl.tk/12390
#---------------------------------------------------------------------
dmproc 1 lz77_decode { data } {
set LZ_Escape1 "\x01"
set LZ_Escape2 "\x02"
set output ""
for {set i 0} {$i < [string length $data]} {incr i} {
set char [string index $data $i]
if { $char eq $LZ_Escape1 } {
set char [string index $data [incr i]]
if { ($char eq $LZ_Escape1) || ($char eq $LZ_Escape2)} {
append output $char
} else {
scan $char %c length
scan [string index $data [incr i]] %c offset
set index [expr {[string length $output] - $offset}]
for {set j 0} {$j < $length} {incr j} {
append output [string index $output $index]
incr index
}
}
} elseif { $char eq $LZ_Escape2 } {
binary scan \
[string range $data [incr i] [incr i]] S length
binary scan \
[string range $data [incr i] [incr i]] S offset
set index [expr {[string length $output] - $offset}]
for {set j 0} {$j < $length} {incr j} {
append output [string index $output $index]
incr index
}
} else {
append output $char
}
}
return $output
}
#---------------------------------------------------------------------
# Routine: makebase64
# Purpose: Sets up a global variable used by "base64_decode"
# Arguments: None
# This routine initializes a global variable (named "base64") that's
# used by "base64_decode". Note: "base64_decode" calls this routine
# automatically if necessary.
# This routine isn't original, but it's believed to be redistributa-
# ble. It's based on Tcl "base64" support code by Stephen Uhler, Brent
# Welch, and Chris Garrigues.
#---------------------------------------------------------------------
dmproc 1 makebase64 {} {
global base64
set i 0
foreach char { \
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
a b c d e f g h i j k l m n o p q r s t u v w x y z \
0 1 2 3 4 5 6 7 8 9 + / \
} {
set base64_tmp($char) $i ; incr i
}
scan z %c len
for { set i 0 } { $i <= $len } { incr i } {
set char [format %c $i]
set val {}
if { [info exists base64_tmp($char)] } {
set val $base64_tmp($char)
} else { set val {} }
lappend base64 $val
}
scan = %c i
set base64 [lreplace $base64 $i $i -1]
unset base64_tmp i char len val
}
#---------------------------------------------------------------------
# Routine: base64_decode
# Purpose: Converts "base64"-encoded data to binary data
# Arguments: string = "base64"-encoded string
# This routine returns the data that the input string represents
# (which may be either binary or text data). Note: Embedded white
# space in the input is ignored.
# This routine isn't original, but it's believed to be redistributa-
# ble. It's based on Tcl "base64" support code by Stephen Uhler, Brent
# Welch, and Chris Garrigues.
#---------------------------------------------------------------------
dmproc 1 base64_decode { string } {
global base64
if { [string length $string] == 0 } { return "" }
if { ![info exists base64] || \
![string length $base64] } { makebase64 }
set output ""
binary scan $string c* X
foreach x $X {
set bits [lindex $base64 $x]
if { $bits >= 0 } {
if { [llength [lappend nums $bits]] == 4 } {
foreach { v w z y } $nums break
set a [expr { ($v << 2) | ($w >> 4) }]
set b [expr { (($w & 0xF) << 4) | ($z >> 2) }]
set c [expr { (($z & 0x3) << 6) | $y }]
append output [binary format ccc $a $b $c]
set nums {}
}
} elseif { $bits == -1 } {
# End of data. Output whatever characters remain. The encoding algor-
# ithm dictates that we can only have 1 or 2 padding characters. If
# x is {}, we have 12 bits of input (enough for one 8-bit output).
# Otherwise, we have 18 bits of input (enough for two 8-bit outputs).
foreach {v w z} $nums break
set a [expr { ($v << 2) | (($w & 0x30) >> 4) }]
if { $z == {} } {
append output [binary format c $a ]
} else {
set b [expr \
{ (($w & 0xF) << 4) | (($z & 0x3C) >> 2) }]
append output [binary format cc $a $b]
}
break
} else {
# Line break or another character that isn't part of the encoded data
# stream. Based on RFC 2045, we should ignore this and we can option-
# ally treat it as a warning or error condition. Presently, this im-
# plementation ignores characters of this type but doesn't produce
# warnings or errors in this case.
continue
}
}
return $output
}
#---------------------------------------------------------------------
# Routine: lz77_base64_decode
# Purpose: Decodes LZ77-compressed base64-encoded data
#
# Arguments: data = Data that was produced by LZ77 compression fol-
# lowed by base64-encoding
# This routine converts the input data from "base64" format to binary,
# decompresses it, and returns the result.
#---------------------------------------------------------------------
dmproc 1 lz77_base64_decode { data } {
return [lz77_decode [base64_decode $data]]
}
#---------------------------------------------------------------------
# Routine: bxdiv_lz77_base64_decode
# Purpose: Decodes bxdiv-LZ77-base64 data (see below)
# Arguments: data = bxdiv-LZ77-base64 data (see below)
# This routine takes data produced by "bxdiv-LZ77-base64" compression-
# encoding as input and returns decoded-decompressed data as output.
# For more information, see the following documentation section:
# bxdiv data format.
#---------------------------------------------------------------------
dmproc 1 bxdiv_lz77_base64_decode { data } {
set data [lz77_base64_decode $data]
set n [binary scan $data a5a6a1a* magic revision divisor data]
if { $n != 4 } { puts "$IE-01: $n" ; exit 1 }
if { $magic ne "bxdiv" } { puts "$IE-02: $magic" ; exit 1 }
scan $divisor %c divisor
if { $divisor < 2 } { return $data }
set str ""
for { set ii 1 } { $ii <= $divisor } { incr ii } {
append str "\\1"
}
regsub -all {(.)} $data "$str" data
return $data
}
#---------------------------------------------------------------------
# Routine: setup_sound_effects_sndname
# Purpose: Sets up one sound effect
# Arguments: sndname = Sound name (omitting "sound_" prefix)
#---------------------------------------------------------------------
dmproc 1 setup_sound_effects_sndname { sndname } {
global gdata
global ${sndname}_bxdiv_lz77_base64
eval set hex $${sndname}_bxdiv_lz77_base64
set temp_bin [bxdiv_lz77_base64_decode $hex]
set gdata(sound_${sndname}) [br::sound load-raw $temp_bin]
unset ${sndname}_bxdiv_lz77_base64
}
#---------------------------------------------------------------------
# Routine: setup_sound_effects
# Purpose: Sets up sound effects
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_sound_effects {} {
global gdata
foreach sound $gdata(list_sounds) {
setup_sound_effects_sndname $sound
}
# Set the default "exit" sound (may be changed as the game progress-
# es).
set gdata(sound_exit) $gdata(sound_poweroff)
# An inter-world portal presently sounds the same as an intra-world
# portal.
set gdata(sound_ocinter) $gdata(sound_ocintra)
}
#---------------------------------------------------------------------
# Routine: setup_audio
# Purpose: Sets up audio (including music and sound effects)
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_audio {} {
global music_lz77_base64
global MusicFile MusicVolume PlayMusic
# Initialize audio
br::audio open speaker
# If music is requested, start playing
# the specified file at the specified
# volume
if { $PlayMusic eq "1" } {
# Play built-in music?
if {$MusicFile eq "internal"} {
# Yes
set MusicDataBinary \
[lz77_base64_decode $music_lz77_base64]
unset music_lz77_base64
br::song play-buffer $MusicDataBinary
} else {
# No - Play an external file
br::song play-file $MusicFile
}
# Adjust volume
if { [info exists MusicVolume] && \
[expr $MusicVolume >= 0] } {
br::song adj-vol $MusicVolume
}
}
setup_sound_effects ; # Set up sound effects
}
#---------------------------------------------------------------------
# Routine: play_sound
# Purpose: Plays a sound
# Arguments: name = Sound name (omitting "sound_" prefix)
# delay = Number passed to "after" (or zero to omit delay)
#---------------------------------------------------------------------
dmproc 1 play_sound { name delay } {
global gdata
if { ![regexp {^sound_} $name] } { set name sound_$name }
if { [info exists gdata($name)] } {
br::sound play $gdata($name)
if { $delay > 0 } { after $delay }
}
}
#---------------------------------------------------------------------
# Routine: quit_program
# Purpose: Quits the program
# Arguments: None
# This routine quits the program. By default, an appropriate "exit"
# sound is played first. To disable the "exit" sound, use:
#
# global gdata
# unset gdata(sound_exit)
#---------------------------------------------------------------------
dmproc 1 quit_program {} {
play_sound exit 2600
exit 0
}
#---------------------------------------------------------------------
# World definitions: Main world.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldMain)
set gdata($World.is_invariant) 0 ; # Flag: Random maps are O.K.
set gdata($World.width) 46 ; # Default map width (in cells)
set gdata($World.height) 32 ; # Default map height (in cells)
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 2 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
; # Worlds that this one connects
; # forward to
set gdata($World.to_worlds) [list \
$gdata(WorldElysian) $gdata(WorldMilk) \
]
set gdata($World.map_data) "" ; # Default map data
append gdata($World.map_data) \
1111111111111111111111111111111111111111111111 \
1----------------------------111-------------1 \
1--111111111-----1-----------111-------------1 \
1--1------------1------------111-------------1 \
1--1-1111111-------------1---111-------------1 \
1--1-1-1------1----1---------111-------------1 \
1--1---1------1----1---------111-------------1 \
1--11111------1-------1------111-------------1 \
1----1----------------1111111111-------------1 \
1----1----111----------------111-------------1 \
1----1---11------------------111-------------1 \
1----1---11-----11-----------11--------------1 \
1----1-----------------------111-----11------1 \
1--------11----------------1111111111111-----1 \
1--------11111111111------1111---------------1 \
1--------------------------1111111111111-----1 \
1---11-----------------------111-------------1 \
1--------1------11---1-------11-------------11 \
1----1--------1-------1------1-----------1--11 \
1--1------------------1------------------1--11 \
1---1----111----------1----------1-------1--11 \
1--------1-----------1---1111------------1--11 \
1-------11------1--------1-------1--1----1--11 \
1---------------------1111---------------1--11 \
1-------------1-------1--1111------1-----1--11 \
1--1111----------1-------1-------1----1--1--11 \
1--1111----1----11-------1------------1--1--11 \
1--1111---------------1111111-------111--1--11 \
1--1111--------111----1--1-------1111----1--11 \
1---------------------1--1-------1-----111--11 \
1----------------1-------1111---------------11 \
1111111111111111111111111111111111111111111111
#---------------------------------------------------------------------
# World definitions: Elysian Fields.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldElysian)
set gdata($World.is_invariant) 0 ; # Flag: Random maps are O.K.
set gdata($World.width) 25 ; # Default map width (in cells)
set gdata($World.height) 20 ; # Default map height (in cells)
; # ockarkinos size class
set gdata($World.ockarkinos_size) "medium"
set gdata($World.occar_minnum) 1 ; # Min. no. of occars
set gdata($World.occar_maxnum) 1 ; # Max. no. of occars
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 1 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
; # Worlds that this one connects
; # forward to
set gdata($World.to_worlds) [list \
$gdata(WorldMilk) $gdata(WorldCaspak) \
]
set gdata($World.map_data) "" ; # Default map data
append gdata($World.map_data) \
1111111111111111111111111 \
1-----------------------1 \
1-----1--------1111---1-1 \
1-----1------111--11111-1 \
1--1111------11---------1 \
1-------------1---------1 \
1--1--------111---------1 \
1-11-------11-----------1 \
1-----------------------1 \
1-11-----111------------1 \
1--11----------111------1 \
1---1-----------11------1 \
1-111----------11-----1-1 \
1------11111--------111-1 \
1------11-----------111-1 \
1-------1---------------1 \
1-------1111---111111---1 \
1--------111---11--11---1 \
1-----------------------1 \
1111111111111111111111111
#---------------------------------------------------------------------
# World definitions: Limbo.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldLimbo)
set gdata($World.is_invariant) 1 ; # Flag: This map is invariant
set gdata($World.width) 25 ; # Default map width (in cells)
set gdata($World.height) 25 ; # Default map height (in cells)
; # No. of karkinos to preload
set gdata($World.ockarkinos_preload) 2
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.occross_maxnum) 1 ; # This world has an occross
set gdata($World.occross_preload) 1
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 1 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
; # Worlds that this one connects
; # forward to (presently none)
set gdata($World.to_worlds) [list]
set gdata($World.map_data) "" ; # Map data
append gdata($World.map_data) "" ; # This map is empty
#---------------------------------------------------------------------
# World definitions: Milk and Honey.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldMilk)
set gdata($World.is_invariant) 0 ; # Flag: Random maps are O.K.
set gdata($World.width) 52 ; # Default map width (in cells)
set gdata($World.height) 30 ; # Default map height (in cells)
set gdata($World.occar_minnum) 1 ; # Min. no. of occars
set gdata($World.occar_maxnum) 1 ; # Max. no. of occars
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 1 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
; # Worlds that this one connects
; # forward to
set gdata($World.to_worlds) [list \
$gdata(WorldLimbo) $gdata(WorldCaspak) \
]
set gdata($World.map_data) "" ; # Default map data
append gdata($World.map_data) \
1111111111111111111111111111111111111111111111111111 \
1--------------------------------------------------1 \
1--11111111----------1---------------------111-----1 \
1-----111111-------------------------------111-----1 \
1--1-------11-----------------------11-----111-----1 \
1---------111----------11---111-----11-------1-----1 \
1--------------------111------1-----11-111---1-----1 \
1------1111---------11------1------------1--11-----1 \
1------------------------11-1-------1---11--1111---1 \
1------1-111--------1---11----------111------1111--1 \
1----------1111-----1-----111---------111------11--1 \
1--------111--1-----1-------1--1----1--------------1 \
1-1--11-------1-----11--111---------111------------1 \
1-1111111--1111-----11----------------111----------1 \
1------11-----111-------11--------------1-----1111-1 \
1-1--------1--111---111--1--------------1111-----1-1 \
1-111-11---11111---1111--1-----------------1-11--1-1 \
1---1-1----11--1---11---11----111111-------1-------1 \
1--------------111-11--11-----1111-1----11-111-----1 \
1----------------------1-----------1---111-------1-1 \
1------------------111-------------1---------11111-1 \
1----------------------------------1-111111--------1 \
1----------------------------------1------1--11----1 \
1-------------------11-------------111----1---1111-1 \
1-----------111---111---------------------11-------1 \
1-------------1----------------------------1-------1 \
1----------1111---111-------------111------1-------1 \
1-------------------1----------------------1---111-1 \
1--------------------------------------------------1 \
1111111111111111111111111111111111111111111111111111
#---------------------------------------------------------------------
# World definitions: Eternia.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldEternia)
set gdata($World.is_invariant) 0 ; # Flag: Random maps are O.K.
set gdata($World.width) 54 ; # Default map width (in cells)
set gdata($World.height) 29 ; # Default map height (in cells)
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 1 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
; # Worlds that this one connects
; # forward to
set gdata($World.to_worlds) [list \
$gdata(WorldCaspak) \
]
set gdata($World.map_data) "" ; # Default map data
append gdata($World.map_data) \
111111111111111111111111111111111111111111111111111111 \
1----------------------------------------------------1 \
1------------1111--11-----------11------111----------1 \
1--------11111111---1-----------11-------------------1 \
1-1------111---11---11------------------1-1111-------1 \
1-1------111----1--111------------------1------------1 \
1-1--------1----1-----------------------1--1111-1111-1 \
1----------1----1-11--------------------11----1-1--1-1 \
1----------1----1-------1111----------1--1-----------1 \
1--------111---------1--1111-----1111---11-----------1 \
1-----111111---------1111--1111-----1---111111-------1 \
1--------111-----------11---111--1--1----11--1111----1 \
1-1---11111-------11-1--------1111-11---------111----1 \
1-1---11111-------11-11-------------------------1----1 \
1-----111------11111--11-----------1------------11---1 \
1----------------------111---------111---------111---1 \
1------------------------11----------1---------111---1 \
1-------------------------1-------1111-----------1---1 \
1------------------------11-------11111----1111111---1 \
1-------------------------------------1--------------1 \
1------1111--------------1--------111-1----1---------1 \
1---------1--------------11---------111----1111111---1 \
1---------111---------11--1---------------111--------1 \
1-11-------11------11111-11--------------11----------1 \
1-11-------11----111-------------------111-----------1 \
1-11--------1---11-------------------111-------------1 \
1-----------1--11------------------------------------1 \
1----------------------------------------------------1 \
111111111111111111111111111111111111111111111111111111
#---------------------------------------------------------------------
# World definitions: Caspak.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldCaspak)
set gdata($World.is_invariant) 1 ; # Flag: This map is invariant
set gdata($World.width) 43 ; # Map width (in cells)
set gdata($World.height) 27 ; # Map height (in cells)
; # No. of karkinos to preload
set gdata($World.ockarkinos_preload) 10
; # ockarkinos size class
set gdata($World.ockarkinos_size) "large"
set gdata($World.ocintra_minnum) 1 ; # Min. no. of ocintra portals
set gdata($World.ocintra_maxnum) 3 ; # Max. no. of ocintra portals
set gdata($World.ocscroll_minnum) 1 ; # Min. no. of ocscrolls
set gdata($World.ocscroll_maxnum) 1 ; # Max. no. of ocscrolls
set gdata($World.octree_minnum) 1 ; # Min. no. of octrees
set gdata($World.octree_maxnum) 3 ; # Max. no. of octrees
set gdata($World.occow_maxnum) 0 ; # This world has no occows
set gdata($World.ocdog_maxnum) 0 ; # This world has no ocdogs
; # Worlds that this one connects
; # forward to
set gdata($World.to_worlds) [list \
$gdata(WorldEternia) \
$gdata(WorldHeaven) \
]
set gdata($World.map_data) "" ; # Map data
append gdata($World.map_data) \
1111111111111111111111111111111111111111111 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1-----------------------------------------1 \
1111111111111111111111111111111111111111111
#---------------------------------------------------------------------
# World definitions: Heaven.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldHeaven)
set gdata($World.is_invariant) 1 ; # Flag: This map is invariant
set gdata($World.width) 19 ; # Map width (in cells)
set gdata($World.height) 13 ; # Map height (in cells)
; # World has one ocflames
set gdata($World.ocflames_minnum) 1
set gdata($World.ocflames_maxnum) 1
set gdata($World.ocflames_preload) 1
set gdata($World.ocintra_maxnum) 0 ; # No ocintra portals
set gdata($World.occow_maxnum) 0 ; # No occows
set gdata($World.ockarkinos_maxnum) 0 ; # No ockarkinos
set gdata($World.ocscroll_maxnum) 0 ; # No ocscrolls
set gdata($World.octree_maxnum) 0 ; # No octrees
; # Worlds this connects forward
; # to
set gdata($World.to_worlds) [list \
$gdata(WorldEndOfAllSongs) \
]
set gdata($World.map_data) "" ; # Map data
append gdata($World.map_data) \
1111111111111111111 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1-----------------1 \
1111111111111111111
#---------------------------------------------------------------------
# World definitions: End of All Songs.
; # "gdata" key prefix string
set World $WorldKeyStart.$gdata(WorldEndOfAllSongs)
set gdata($World.is_invariant) 1 ; # Flag: This map is invariant
set gdata($World.width) 20 ; # Map width (in cells)
set gdata($World.height) 15 ; # Map height (in cells)
set gdata($World.occow_maxnum) 0 ; # Zero occows
set gdata($World.ocintra_maxnum) 0 ; # Zero ocintra portals
set gdata($World.ocscroll_maxnum) 0 ; # Zero ocscrolls
set gdata($World.octree_maxnum) 0 ; # Zero octrees
; # Worlds that this one connects
; # forward to (none)
set gdata($World.to_worlds) [list]
set gdata($World.map_data) "" ; # Map data
append gdata($World.map_data) "" ; # This map is empty
#---------------------------------------------------------------------
# Routine: random_direction
# Purpose: Returns a random direction number
# Arguments: None
# "random_direction" returns a random direction number. For the pur-
# poses of this routine, directions are numbered from 0 to 7 and
# should be interpreted as follows:
#
# 0: NorthWest 3: North 5: NorthEast
# 1: West 6: East
# 2: SouthWest 4: South 7: SouthEast
#---------------------------------------------------------------------
dmproc 10 random_direction {} { return [expr { int (rand() * 8) }] }
#---------------------------------------------------------------------
# Routine: (xproc) get_dir_vx_vy
# Purpose: Translates a direction number to X-Y deltas
# Arguments: dir = Direction number (see below)
# vx = X-delta output (passed by reference)
# vy = Y-delta output (passed by reference)
# This is an "xproc" routine; i.e., it supports "&variable"-style
# pass-by-reference.
# "dir" should be a direction number of the type returned by "random_
# direction".
# This routine translates "dir" to X-Y deltas that represent a step of
# one unit in the specified direction. It sets vx (in the caller's
# scope) to the resulting X-delta (-1, 0, or 1). It sets vy (in the
# caller's scope) to the resulting Y-delta (-1, 0, or 1).
#---------------------------------------------------------------------
if { $DebugLevel > 1 } { puts "define get_dir_vx_vy" }
xproc get_dir_vx_vy { dir &vx &vy } {
switch $dir {
0 { set vx -1; set vy -1 }
1 { set vx -1; set vy 0 }
2 { set vx -1; set vy 1 }
3 { set vx 0; set vy -1 }
4 { set vx 0; set vy 1 }
5 { set vx 1; set vy -1 }
6 { set vx 1; set vy 0 }
7 { set vx 1; set vy 1 }
}
}
#---------------------------------------------------------------------
# Routine: random_int
# Purpose: Returns a random integer in a specified range
# Arguments: min_int = First (lower ) integer in a range
# max_int = Last (higher) integer in a range
# "random_int" returns a random integer that ranges from $min_int to
# $max_int inclusive. Special case: If $max_int is less than $min_int,
# this routine returns $max_int.
#---------------------------------------------------------------------
dmproc 10 random_int { min_int max_int } {
set min_int [expr { int ($min_int + 0.5) }]
set max_int [expr { int ($max_int + 0.5) }]
if { $max_int < $min_int } { return $max_int }
set num_int [expr $max_int - $min_int + 1]
return [expr { $min_int + int (rand() * $num_int) }]
}
#---------------------------------------------------------------------
# Routine: random_real
# Purpose: Returns a random real in a specified range
# Arguments: min_real = First (lower ) real in a range
# max_real = Last (higher) real in a range
# "random_real" returns a random real that ranges from just over $min_
# real to just under $max_real. Special case: If $max_real is less
# than or equal to $min_real, this routine returns $max_real.
#---------------------------------------------------------------------
dmproc 10 random_real { min_real max_real } {
if { $max_real <= $min_real } { return $max_real }
set delta [expr $max_real - $min_real]
return [expr { $min_real + (rand() * $delta) }]
}
#---------------------------------------------------------------------
# Routine: get_sprite_class
# Purpose: Returns a sprite's object-class name
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 10 get_sprite_class { id } {
global sdata
if { ![info exists sdata($id.)] } { puts "$IE-01" ; exit 1 }
set callback $sdata($id.)
if { ![regexp {^run_} $callback] } { puts "$IE-02" ; exit 1 }
regsub {^run_} $callback "" objclass
return $objclass
}
#---------------------------------------------------------------------
# Routine: get_world_param
# Purpose: Retrieves a world-specific variable
# Arguments: name = Variable name
# If the given variable has been set in the context of the current
# world, this routine returns the stored world-specific value. Note:
# This may be anything; an integer, a list, etc.
# Otherwise, this routine returns the integer 0.
#---------------------------------------------------------------------
dmproc 100 get_world_param { name } {
if { $DebugLevel >= 2 } { puts "$rtn $name" }
global gdata lv WorldKeyStart
set world_key $WorldKeyStart.$lv
if [info exists gdata($world_key.$name)] {
return $gdata($world_key.$name)
}
return 0
}
#---------------------------------------------------------------------
# Routine: set_world_param
# Purpose: Sets a world-specific variable
# Arguments: name = Variable name
# value = Arbitrary value
# This routine sets the given variable to the specified value for the
# current world. Instances of the variable that were set in the con-
# texts of other worlds aren't affected.
#---------------------------------------------------------------------
dmproc 100 set_world_param { name value } {
if { $DebugLevel >= 2 } { puts "$rtn $name" }
global gdata lv WorldKeyStart
set world_key $WorldKeyStart.$lv
set gdata($world_key.$name) $value
}
#---------------------------------------------------------------------
# Routine: get_class_param
# Purpose: Retrieves a specified parameter based on context
# Arguments: objclass = Object-class name
# param = Parameter name
# "param" may be the name of any parameter that may be associated with
# objects in the specified class. Examples include "maxnum", "zhint",
# etc.
# If the given parameter has been set for the given object class in
# the context of the current world, this routine returns the stored
# world-specific value. Note: This may be anything; an integer, a
# list, etc.
# Otherwise, if a global default setting exists for the given object
# class, this routine returns the stored global default value.
# Otherwise, this routine returns the integer 0.
# For more information about global and per-world parameters, see
# "Object-class parameters".
#---------------------------------------------------------------------
dmproc 100 get_class_param { objclass param } {
if { $DebugLevel >= 2 } { puts "$rtn $objclass $param" }
global gdata lv WorldKeyStart
if { [info exists lv] } {
set world_key $WorldKeyStart.$lv
if [info exists gdata($world_key.${objclass}_${param})] {
return $gdata($world_key.${objclass}_${param})
}
}
if { [info exists gdata(Default_${objclass}_${param})] } {
return $gdata(Default_${objclass}_${param})
}
return 0
}
#---------------------------------------------------------------------
# Routine: get_class_counter
# Purpose: Per-world sprite counter utility routine
# Arguments: objclass = Object-class name
# This routine returns the value of a counter that tracks the number
# of instances of the specified object class that exist in the current
# world.
# The counter doesn't need to be initialized. If it doesn't already
# exist, this routine creates it.
#---------------------------------------------------------------------
dmproc 10 get_class_counter { objclass } {
global gdata lv
if { ![info exists gdata($lv,num_$objclass)] } {
set gdata($lv,num_$objclass) 0
}
return $gdata($lv,num_$objclass)
}
#---------------------------------------------------------------------
# Routine: get_class_counter
# Purpose: Per-world sprite counter utility routine
# Arguments: objclass = Object-class name
# This routine returns the value of a counter that tracks the number
# of instances of the specified object class that exist in the current
# world.
# The counter doesn't need to be initialized. If it doesn't already
# exist, this routine creates it.
#---------------------------------------------------------------------
dmproc 10 get_class_counter { objclass } {
global gdata lv
if { ![info exists gdata($lv,num_$objclass)] } {
set gdata($lv,num_$objclass) 0
}
return $gdata($lv,num_$objclass)
}
#---------------------------------------------------------------------
# Routine: incr_class_counter
# Purpose: Per-world sprite counter utility routine
# Arguments: objclass = Object-class name
# This routine increments a counter that tracks the number of in-
# stances of the specified object class that exist in the current
# world and returns the result.
#---------------------------------------------------------------------
dmproc 10 incr_class_counter { objclass } {
global gdata lv
set n [expr [get_class_counter $objclass] + 1]
set gdata($lv,num_$objclass) $n
return $n
}
#---------------------------------------------------------------------
# Routine: decr_class_counter
# Purpose: Per-world sprite counter utility routine
# Arguments: objclass = Object-class name
# This routine decrements a counter that tracks the number of in-
# stances of the specified object class that exist in the current
# world and returns the result.
#---------------------------------------------------------------------
dmproc 10 decr_class_counter { objclass } {
global gdata lv
set n [expr [get_class_counter $objclass] - 1]
if { $n < 0 } { set n 0 }
set gdata($lv,num_$objclass) $n
return $n
}
#---------------------------------------------------------------------
# Routine: get_object_param
# Purpose: Gets a specified variable for a specified object
# Arguments: id = Sprite ID
# name = Flag name
# This routine returns the value of the specified variable for the
# object associated with the specified sprite. The variable doesn't
# need to be initialized; if it doesn't already exist, this routine
# initializes it to the integer 0.
#---------------------------------------------------------------------
dmproc 10 get_object_param { id name } {
global sdata
if { ![info exists sdata($id.$name)] } {
set sdata($id.$name) 0
}
return $sdata($id.$name)
}
#---------------------------------------------------------------------
# Routine: set_object_param
# Purpose: Sets a specified variable for a specified object
# Arguments: id = Sprite ID
# name = Flag name
# value = Arbitrary value
# This routine sets the specified variable for the object associated
# with the specified sprite to the specified value. It returns the
# value in question.
#---------------------------------------------------------------------
dmproc 10 set_object_param { id name value } {
global sdata
set sdata($id.$name) $value
return $value
}
#---------------------------------------------------------------------
# Routine: get_object_name_random
# Purpose: Selects a random name
# Arguments: objclass = Object-class name
# If a list of possible names has been specified for the given object
# class, this routine returns a random name from the list. Otherwise,
# this routine returns the string "none".
# Note: Name lists specified at the world-definitions level take pre-
# cedence over name lists specified at the global level.
# For more information about global and per-world parameters, see
# "Object-class parameters".
#---------------------------------------------------------------------
dmproc 10 get_object_name_random { objclass } {
set name_list [get_class_param $objclass name]
if { $name_list eq "0" } { return "none" }
set name [lrandom $name_list]
regsub -all {\*} $name "" name
return $name
}
#---------------------------------------------------------------------
# Routine: get_object_name_current
# Purpose: Gets the game-level name of an individual sprite
# Arguments: id = Sprite ID
# If the specified sprite was assigned a game-level name when it was
# created, this routine returns the name. Otherwise, this routine re-
# returns the string "none".
#---------------------------------------------------------------------
dmproc 10 get_object_name_current { id } {
global sdata
if { ![info exists sdata($id.name)] } { return "none" }
return $sdata($id.name)
}
#---------------------------------------------------------------------
# Routine: destroy_sprite
# Purpose: Destroys a specified sprite
# Arguments: objclass = Object-class name
# id = Sprite ID
# The specified sprite must exist in the current world. Additionally,
# it must be of the specified class. This routine destroys the sprite
# at all applicable code levels.
#---------------------------------------------------------------------
dmproc 10 destroy_sprite { objclass id } {
global gdata layers lv sdata
set callback $sdata($id.)
# Consistency check
if { $callback ne "run_$objclass" } { puts "$IE-01" ; exit 1 }
br::list remove $layers($lv.spr-list) $id
br::sprite delete $id
array unset sdata $id.*
decr_class_counter $objclass
# Remove any associated collision lock
set ocplayer_id $gdata($lv,ocplayer_id)
set xlock_id $ocplayer_id.${objclass}_id
if { [info exists gdata($lv,$xlock_id)] } {
unset gdata($lv,$xlock_id)
}
}
#---------------------------------------------------------------------
# Routine: verify_sprite_exists
# Purpose: Used for sanity checks
# Arguments: msg = Base message string
# id = Sprite ID
# This routine verifies that the specified sprite exists in the cur-
# rent world. To do this, it checks for the existence of:
#
# sdata($id.)
# If the sprite doesn't exist, this routine prints an error message
# and terminates the caller. The error message includes both $msg and
# $id.
#---------------------------------------------------------------------
dmproc 10 verify_sprite_exists { msg id } {
global lv sdata
if { ![info exists sdata($id.)] } {
puts "$IE-01: $msg lv=$lv id=$id" ; exit 1
}
}
#---------------------------------------------------------------------
# Routine: collision_sprites
# Purpose: Sprite collision utility routine
# Arguments: id = Sprite ID
# This routine returns a list of zero or more sprite IDs for sprites
# that presently overlap the specified sprite.
#---------------------------------------------------------------------
dmproc 10 collision_sprites { id } {
global layers lv
return [br::collision sprites $id $layers($lv.spr-list)]
}
#---------------------------------------------------------------------
# Routine: inventory_ocmoney_get
# Purpose: Returns player's ocmoney counter
# Arguments: None
#---------------------------------------------------------------------
dmproc 10 inventory_ocmoney_get {} {
global gdata
if { ![info exists gdata(ocmoney)] } { set gdata(ocmoney) 0 }
return $gdata(ocmoney)
}
#---------------------------------------------------------------------
# Routine: inventory_ocmoney_add
# Purpose: Increments player's ocmoney counter
# Arguments: num = Number to add to ocmoney counter (may be negative)
#---------------------------------------------------------------------
dmproc 10 inventory_ocmoney_add { num } {
global gdata
inventory_ocmoney_get
incr gdata(ocmoney) $num
}
#---------------------------------------------------------------------
# Routine: inventory_get
# Purpose: Gets description of player's inventory
# Arguments: None
# This routine returns a list that describes the player's current in-
# ventory. The list contains one entry per item (treating groups simi-
# lar to "50 gold coins" as single items). If the inventory is empty,
# the list returned contains the single string "Empty".
#---------------------------------------------------------------------
dmproc 10 inventory_get {} {
set iv [list]
set n [inventory_ocmoney_get]
if { $n > 0 } { lappend iv "$n gold coins" }
if { [llength $iv] == 0 } { lappend iv Empty }
return $iv
}
#---------------------------------------------------------------------
# Routine: show_msg
# Purpose: Displays a message and waits for a keypress
# Arguments: text = Message text (may be multi-line)
#---------------------------------------------------------------------
dmproc 1 show_msg { text xpos ypos } {
global layers lv
global KeyH_Button KeyH_Input
global KeyI_Button KeyI_Input
global KeyQ_Button KeyQ_Input
set stglist [list]
foreach line [split $text "\n"] {
regsub -all {\015*\012} $line "" line
set stg [br::string create]
set stglist [concat $stglist $stg]
br::string position $stg $xpos $ypos
br::string text $stg $line
br::list add $layers($lv.str-list) $stg
incr ypos 8
}
set done 0
set arrow_state 1
while { $done < 1 } {
set io(1) [br::io fetch 1]
set io(0) [br::io fetch 0]
set hkey [lindex $io($KeyH_Input) 2 $KeyH_Button]
set ikey [lindex $io($KeyI_Input) 2 $KeyI_Button]
set qkey [lindex $io($KeyQ_Input) 2 $KeyQ_Button]
if { [lindex $io(0) 7] || $qkey || \
[br::io has-quit] } { quit_program }
if { [lindex $io(0) 5] } { set done 1 }
set horiz [lindex $io(0) 0 0]
set vert [lindex $io(0) 0 1]
set vx [expr { $horiz < 0 ? -1 : ($horiz > 0 ? 1 : 0) }]
set vy [expr { $vert < 0 ? -1 : ($vert > 0 ? 1 : 0) }]
# Undocumented feature: Some regular keys will close the displayed
# message.
if { $vx || $vy || $hkey || $ikey } {
if { $arrow_state == 2 } { set done 1 }
} else {
set arrow_state 2
}
br::render display
after 25
}
foreach stg $stglist {
br::list remove $layers($lv.str-list) $stg
br::string delete $stg
}
after 250 ; # Allow a moment for key to be releas-
; # ed
}
#---------------------------------------------------------------------
# Routine: display_help
# Purpose: Displays runtime-help message
# Arguments: None
# This routine displays the program's runtime-help message and waits
# for a keypress. Enter (or an arrow key) causes a return to normal
# operation. Escape or Q quits the program.
#---------------------------------------------------------------------
dmproc 1 display_help {} {
set msg ""
append msg \
" Help: H Inventory: I \r\n" \
" Pause: H \r\n" \
" Quit: Escape or Q \r\n" \
" \r\n" \
" Press Enter or arrow to resume "
show_msg $msg 10 50
}
#---------------------------------------------------------------------
# Routine: display_inventory
# Purpose: Displays the player's inventory
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 display_inventory {} {
set InventoryTextWidth 30
set msg ""
set inventory [inventory_get]
lappend inventory ""
lappend inventory "Press Enter or arrow to resume"
foreach entry $inventory {
append msg [format " %-${InventoryTextWidth}s \r\n" $entry]
}
show_msg $msg 10 50
}
#---------------------------------------------------------------------
# Routine: display_msg_startup
# Purpose: Displays startup-time message
# Arguments: None
# This routine displays the program's startup-time message and waits
# for a keypress. Enter (or an arrow key) causes a return to normal
# operation. Escape or Q quits the program.
#---------------------------------------------------------------------
dmproc 1 display_msg_startup {} {
global NameAndRevision
set FmtName [format " %-20s " $NameAndRevision]
set msg ""
append msg \
"$FmtName\r\n" \
"\r\n" \
" Welcome to Hell! \r\n" \
" Use arrow keys to \r\n" \
" move, Ctrl or Space \r\n" \
" to shoot, and Esc or \r\n" \
" Q to quit. Find the \r\n" \
" exit to win. \r\n" \
"\r\n" \
" Press Enter to begin "
show_msg $msg 75 70
}
#---------------------------------------------------------------------
# Routine: display_wisdom
# Purpose: Displays random wisdom
# Arguments: None
# This routine displays a random quote (or "fortune") and waits for a
# keypress. Enter (or an arrow key) causes a return to normal opera-
# tion. Escape or Q quits the program.
#---------------------------------------------------------------------
dmproc 1 display_wisdom {} {
global wisdom_list wisdom_num wisdom_lz77_base64
if { ![info exists wisdom_list] } {
set wisdom_txt [lz77_base64_decode $wisdom_lz77_base64]
regsub -all {\n%%\n} $wisdom_txt "\001" wisdom_txt
regsub -all {\015*\012} $wisdom_txt "\012" wisdom_txt
regsub -all {[\001]+$} $wisdom_txt "" wisdom_txt
set wisdom_list [split $wisdom_txt "\001"]
set wisdom_num [llength $wisdom_list]
}
set wisdom_idx [expr [random_int 1 $wisdom_num] - 1]
set text [lindex $wisdom_list $wisdom_idx]
set lines [split $text "\012"]
set msg ""
append msg [format " %-36s\r\n" "Contents of scroll:" ]
append msg [format " %-36s\r\n" "" ]
foreach line $lines {
append msg [format " %-36s" $line]
append msg "\r\n"
}
append msg [format " %-36s\r\n" "" ]
append msg [format " %-36s\r\n" "Press Enter to continue" ]
show_msg $msg 10 60
}
#---------------------------------------------------------------------
# Routine: (xproc) get_target_dx_dy
# Purpose: Gets X-Y sprite deltas
# Arguments: base_id = Sprite ID for a base object
# target_id = Sprite ID for a target object
# dx = X output (passed by reference)
# dy = Y output (passed by reference)
# This is an "xproc" routine; i.e., it supports "&variable"-style
# pass-by-reference.
# This routine sets dx (in the caller's scope) equal to the target ob-
# ject's X-coordinate minus the base object's X-coordinate. It also
# sets dy (in the caller's scope) equal to the target object's Y-
# coordinate minus the base object's Y-coordinate.
#---------------------------------------------------------------------
if { $DebugLevel > 1 } { puts "define get_target_dx_dy" }
xproc get_target_dx_dy { base_id target_id &dx &dy } {
global sdata
set base_position [br::sprite pos $base_id]
set base_x [lindex $base_position 0]
set base_y [lindex $base_position 1]
set target_x $sdata($target_id.px)
set target_y $sdata($target_id.py)
set dx [expr $target_x - $base_x]
set dy [expr $target_y - $base_y]
}
#---------------------------------------------------------------------
# Routine: random_position_sprite
# Purpose: Randomly positions a sprite
# Arguments: id = Sprite ID
# This routine randomly positions the specified sprite. The new loca-
# tion is guaranteed not to intersect any walls. Additionally, the
# move is guaranteed not to produce a collision where either sprite
# involved belongs to any of the classes listed in "list_classes_
# bounce".
#---------------------------------------------------------------------
dmproc 5 random_position_sprite { id } {
global gdata layers lv sdata
verify_sprite_exists $rtn $id
set isolate_this 0 ; # Flag: Must isolate this object
; # Get sprite class
set objclass [get_sprite_class $id]
# Future change: This loop could prob-
# ably be replaced with "lsearch" code
foreach callbase $gdata(list_classes_bounce) {
if { $objclass eq $callbase } { set isolate_this 1 }
}
# Is position predetermined?
set forceposn [get_object_param $id forceposn]
# The loop-count limit used here is arbitrary. However, it should be
# an integer, and it should probably lie somewhere in the range of 100
# to 1000.
for { set ii 1 } { $ii <= 500 } { incr ii } {
if { $forceposn > 0 } {
set xpos [get_object_param $id xpos]
set ypos [get_object_param $id ypos]
} else {
set xpos [expr { int(rand()*($layers($lv.width) * 8)) }]
set ypos [expr { int(rand()*($layers($lv.height) * 8)) }]
}
br::sprite pos $id $xpos $ypos
if { [lindex [br::collision map \
$id $layers($lv.map) 1] 0] } { continue }
set okay 1
set nobounce1 [get_object_param $id nobounce]
foreach tgt [collision_sprites $id] {
if { $nobounce1 } { break }
set tgt_id [lindex $tgt 1]
set otherclass [get_sprite_class $tgt_id]
set nobounce2 [get_object_param $tgt_id nobounce]
if { $nobounce2 } { continue }
if { $isolate_this } { set okay 0 }
if { !$okay } { break }
foreach callbase $gdata(list_classes_bounce) {
if { $otherclass eq $callbase } {
set okay 0 ; break
}
}
}
if { $okay } { return }
}
puts "$IE-01" ; # Shouldn't reach this point
exit 1
}
#---------------------------------------------------------------------
# Routine: handle_limbo
# Purpose: Handles a special case
# Arguments: id = Sprite ID
# This is a support routine for "move_sprite" and "run_ocplayer". It
# handles special cases related to the Limbo world and/or situations
# where the player can travel through walls.
#---------------------------------------------------------------------
dmproc 10 handle_limbo { id } {
global gdata layers lv sdata WorldKeyStart
if { ![info exists gdata($lv.is_empty)] || \
!$gdata($lv.is_empty) } {
set ocplayer_id $gdata($lv,ocplayer_id)
if { $id ne $ocplayer_id } { return }
if { ![is_ocplayer_driving] } { return }
}
while { 1 } {
set max_x [expr ($layers($lv.width) * 8) - 1]
set max_y [expr ($layers($lv.height) * 8) - 1]
set MyPosition [br::sprite pos $id]
set my_x [lindex $MyPosition 0]
set my_y [lindex $MyPosition 1]
if { ($my_x > 0) && ($my_x < $max_x) && \
($my_y > 0) && ($my_y < $max_y) } { return }
random_position_sprite $id
}
}
#---------------------------------------------------------------------
# Routine: move_sprite
# Purpose: Moves an autonomous sprite
# Arguments: id = Sprite ID
# $id should specify a sprite ID for a mobile autonomous sprite; i.e.,
# a mobile non-player sprite such as an ockarkinos, an ocmedical, or
# an ocbullet.
# This routine uses "br::motion single" to move the specified sprite.
# It also handles some special cases (through a call to "handle_
# limbo"; for more information, see that routine).
#---------------------------------------------------------------------
dmproc 100 move_sprite { id } {
br::motion single $id
handle_limbo $id
}
#---------------------------------------------------------------------
# Routine: setup_map_fixup_table
# Purpose: Support routine for "make_random_map"
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_map_fixup_table {} {
global MapFixupTable
set MapFixupTable(initialized) 1
set MapFixupTable(1--1) { ; # Edits a 2x2 square
set x0y0 "-" ; set newmap($x0:$y0) "-"
}
set MapFixupTable(-11-) { ; # Edits a 2x2 square
set x0y1 "-" ; set newmap($x0:$y1) "-"
}
set MapFixupTable(-11-1-) { ; # Edits a 3x2 rectangle
set x1y0 "-" ; set newmap($x1:$y0) "-"
}
set MapFixupTable(1111-1111) { ; # Edits a 3x3 square
set x1y1 "1" ; set newmap($x1:$y1) "1"
}
set MapFixupTable(11-1-1111) { ; # Edits a 3x3 square
set x1y1 "1" ; set newmap($x1:$y1) "1"
}
set MapFixupTable(1-1111---) { ; # Edits a 3x3 square
set x1y1 "1" ; set newmap($x1:$y1) "1"
}
set MapFixupTable(1--1-1111) { ; # Edits a 3x3 square
set x1y0 "-" ; set newmap($x1:$y0) "-"
}
set MapFixupTable(1-11-1111) { ; # Edits a 3x3 square
set x0y1 "-" ; set newmap($x0:$y1) "-"
set x2y1 "-" ; set newmap($x2:$y1) "-"
}
set MapFixupTable(11111--11111) { ; # Edits a 4x3 rectangle
set x1y1 "1" ; set newmap($x1:$y1) "1"
set x2y1 "1" ; set newmap($x2:$y1) "1"
}
set MapFixupTable(1-11111-----) { ; # Edits a 4x3 rectangle
set x2y1 "-" ; set newmap($x2:$y1) "-"
}
set MapFixupTable(----111-1-11) { ; # Edits a 4x3 rectangle
set x2y1 "-" ; set newmap($x2:$y1) "-"
}
set MapFixupTable(11--1111----) { ; # Edits a 4x3 rectangle
set x1y1 "-" ; set newmap($x1:$y1) "-"
}
set MapFixupTable(----111---1---11) { ; # Edits a 4x4 square
set x1y1 "-" ; set newmap($x1:$y1) "-"
}
set MapFixupTable(111---1--11-11--) { ; # Edits a 4x4 square
set x1y2 "-" ; set newmap($x1:$y2) "-"
}
set MapFixupTable(111---1--11-1---) { ; # Edits a 4x4 square
set x1y2 "-" ; set newmap($x1:$y2) "-"
}
set MapFixupTable(-11---1-111-----) { ; # Edits a 4x4 square
set x2y1 "-" ; set newmap($x2:$y1) "-"
}
set MapFixupTable(-1---1---111----) { ; # Edits a 4x4 square
set x2y2 "-" ; set newmap($x2:$y2) "-"
}
set MapFixupTable(11111--11--11111) { ; # Edits a 4x4 square
set x1y1 "1" ; set newmap($x1:$y1) "1"
set x1y2 "1" ; set newmap($x1:$y2) "1"
set x2y1 "1" ; set newmap($x2:$y1) "1"
set x2y2 "1" ; set newmap($x2:$y2) "1"
}
}
#---------------------------------------------------------------------
# Routine: make_random_map
# Purpose: Creates a random map
# Arguments: width = Map width (in cells)
# height = Map height (in cells)
# "make_random_map" returns a random map as a text string. The string
# contains $width * $height characters. Each character indicates the
# contents of one map cell. "1" represents a wall cell and "-" repre-
# sents an empty cell.
# The string is organized as follows: row 1, col 1; row 1, col 2; etc.
# through row 1, col $width; row 2, col 1; row 2, col 2; etc. through
# row $height, col $width.
#---------------------------------------------------------------------
dmproc 1 make_random_map { width height } {
global RandomMapFollow RandomMapPoints
global RandomMapMinSep1 RandomMapMinSep2
global MapFixupTable
if { ![info exists MapFixupTable(initialized)] } {
setup_map_fixup_table
}
set area [expr $width * $height]
set np [expr int (($area / 100) * $RandomMapPoints)]
for { set y 1 } { $y <= $height } { incr y } {
for { set x 1 } { $x <= $width } { incr x } {
set newmap($x:$y) "-"
}
}
for { set x 1 } { $x <= $width } { incr x } \
{ set newmap($x:1) "1" }
for { set x 1 } { $x <= $width } { incr x } \
{ set newmap($x:$height) "1" }
for { set y 1 } { $y <= $height } { incr y } \
{ set newmap(1:$y) "1" }
for { set y 1 } { $y <= $height } { incr y } \
{ set newmap($width:$y) "1" }
for { set p 1 } { $p <= $np } { incr p } {
set x [expr 1 + int (rand() * $width )]
set y [expr 1 + int (rand() * $height )]
set nf [expr 1 + int (rand() * $RandomMapFollow )]
set reject 0
if { ($x == 1) || ($x == $width ) ||
($y == 1) || ($y == $height) } { continue }
for { set dx -$RandomMapMinSep1 } \
{ $dx <= $RandomMapMinSep1 } { incr dx } {
for { set dy -$RandomMapMinSep1 } \
{ $dy <= $RandomMapMinSep1 } { incr dy } {
set nx [expr $x + $dx]
set ny [expr $y + $dy]
if { ($nx <= 1) || ($nx >= $width ) ||
($ny <= 1) || ($ny >= $height ) } {
continue
}
if { $newmap($nx:$ny) eq "1" } {
set reject 1; break
}
}
}
if {$reject} { continue }
set newmap($x,$y) "1"
for { set i 1 } { $i < $nf } { incr i } {
set reject 0
set dir [expr int (rand() * 4)]
switch $dir {
0 { set vx -1; set vy 0 }
1 { set vx 1; set vy 0 }
2 { set vx 0; set vy -1 }
3 { set vx 0; set vy 1 }
}
if { $vx < 0 } {
set xa [expr $x - 1]
set xz [expr $x - $RandomMapMinSep2]
if { $xa <= 1 } { set reject 1; break }
for { set nx $xa } { $nx >= $xz } { incr nx -1 } {
if { $nx <= 1 } { break }
set ya [expr $y - 1]; set yz [expr $y + 1]
for { set ny $ya } { $ny < $yz } { incr ny } {
if { ($ny >= 1) && ($ny <= $height) &&
$newmap($nx:$ny) eq "1" } {
set reject 1; break
}
}
}
}
if { $vx > 0 } {
set xa [expr $x + 1]
set xz [expr $x + $RandomMapMinSep2]
if { $xa >= $width } { set reject 1; break }
for { set nx $xa } { $nx < $xz } { incr nx } {
if { $nx >= $width } { break }
set ya [expr $y - 1]; set yz [expr $y + 1]
for { set ny $ya } { $ny < $yz } { incr ny } {
if { ($ny >= 1) && ($ny <= $height) &&
$newmap($nx:$ny) eq "1" } {
set reject 1; break
}
}
}
}
if { $vy < 0 } {
set ya [expr $y - 1]
set yz [expr $y - $RandomMapMinSep2]
if { $ya <= 1 } { set reject 1; break }
for { set ny $ya } { $ny >= $yz } { incr ny -1 } {
if { $ny <= 1 } { break }
set xa [expr $x - 1]; set xz [expr $x + 1]
for { set nx $xa } { $nx < $xz } { incr nx } {
if { ($nx >= 1) && ($nx <= $width) &&
$newmap($nx:$ny) eq "1" } {
set reject 1; break
}
}
}
}
if { $vy > 0 } {
set ya [expr $y + 1]
set yz [expr $y + $RandomMapMinSep2]
if { $ya >= $width } { set reject 1; break }
for { set ny $ya } { $ny < $yz } { incr ny } {
if { $ny >= $height } { break }
set xa [expr $x - 1]; set xz [expr $x + 1]
for { set nx $xa } { $nx < $xz } { incr nx } {
if { ($nx >= 1) && ($nx <= $width) &&
$newmap($nx:$ny) eq "1" } {
set reject 1; break
}
}
}
}
if { $reject == 0 } {
incr x $vx
incr y $vy
set newmap($x:$y) "1"
}
if { ($x <= 1) || ($x >= $width ) ||
($y <= 1) || ($y >= $height) } { break }
}
}
set hm1 [expr $height - 1]
set wm1 [expr $width - 1]
for { set x 2 } { $x < $width } { incr x } \
{ set newmap($x:2) "-" }
for { set x 2 } { $x < $width } { incr x } \
{ set newmap($x:$hm1) "-" }
for { set y 2 } { $y < $height } { incr y } \
{ set newmap(2:$y) "-" }
for { set y 2 } { $y < $height } { incr y } \
{ set newmap($wm1:$y) "-" }
set pass 0
set retry 1
if { $DebugLevel > 1 } {
set tmpx ""
for { set y 1 } { $y <= $height } { incr y } {
for { set x 1 } { $x <= $width } { incr x } {
append tmpx $newmap($x:$y)
}
append tmpx "\n"
}
puts "$rtn: pre-cleanup map:"
puts $tmpx
}
while 1 {
incr pass
if { ($retry == 0) || ($pass > 3) } { break }
set retry 0
for { set y 3 } { $y < $height } { incr y } {
for { set x 3 } { $x < $width } { incr x } {
set x3y3 "?" ; set x2y3 "?" ; set x1y3 "?" ; set x0y3 "?"
set x3y2 "?" ; set x2y2 "?" ; set x1y2 "?" ; set x0y2 "?"
set x3y1 "?" ; set x2y1 "?" ; set x1y1 "?" ; set x0y1 "?"
set x3y0 "?" ; set x2y0 "?" ; set x1y0 "?" ; set x0y0 "?"
set x0 $x
set y0 $y
set x1 [expr $x - 1]
set y1 [expr $y - 1]
set x2 [expr $x - 2]
set y2 [expr $y - 2]
set x1y1 $newmap($x1:$y1)
set x0y1 $newmap($x0:$y1)
set x1y0 $newmap($x1:$y0)
set x0y0 $newmap($x0:$y0)
set x2y2 $newmap($x2:$y2)
set x2y1 $newmap($x2:$y1)
set x2y0 $newmap($x2:$y0)
set x1y2 $newmap($x1:$y2)
set x0y2 $newmap($x0:$y2)
if { $x > 3 } {
set x3 [expr $x - 3]
set x3y2 $newmap($x3:$y2)
set x3y1 $newmap($x3:$y1)
set x3y0 $newmap($x3:$y0)
}
if { $y > 3 } {
set y3 [expr $y - 3]
set x2y3 $newmap($x2:$y3)
set x1y3 $newmap($x1:$y3)
set x0y3 $newmap($x0:$y3)
}
if { ($x > 3) && ($y > 3) } {
set x3 [expr $x - 3]
set y3 [expr $y - 3]
set x3y3 $newmap($x3:$y3)
}
# The cleanup algorithm used below is based on squares and horizontal
# rectangles. Presently, there's no provision for working with verti-
# cal rectangles here.
# BlockOf04 is a string that represents the 2x2 square of 4 characters
# whose lower-right corner is at $x,$y.
# BlockOf06H is a string that represents the 3x2 horizontal rectangle
# of 6 characters whose lower-right corner is at $x,$y.
# BlockOf09 is a string that represents the 3x3 square of 9 characters
# whose lower-right corner is at $x,$y.
# BlockOf12H is a string that represents the 4x3 horizontal rectangle
# of 12 characters whose lower-right corner is at $x,$y.
# BlockOf16 is a string that represents the 4x4 square of 16 charac-
# ters whose lower-right corner is at $x,$y.
# The loop-count limit used here is arbitrary. However, it should be
# an integer, and it should probably lie somewhere in the range of 25
# to 100.
for { set ii 1 } { $ii <= 50 } { incr ii } {
set BlockOf04 ""
append BlockOf04 $x1y1 $x0y1 \
$x1y0 $x0y0
set BlockOf06H ""
append BlockOf06H $x2y1 $x1y1 $x0y1 \
$x2y0 $x1y0 $x0y0
set BlockOf09 ""
append BlockOf09 $x2y2 $x1y2 $x0y2 \
$x2y1 $x1y1 $x0y1 \
$x2y0 $x1y0 $x0y0
set BlockOf12H ""
append BlockOf12H $x3y2 $x2y2 $x1y2 $x0y2 \
$x3y1 $x2y1 $x1y1 $x0y1 \
$x3y0 $x2y0 $x1y0 $x0y0
set Blockof16 ""
append BlockOf16 $x3y3 $x2y3 $x1y3 $x0y3 \
$x3y2 $x2y1 $x1y2 $x0y1 \
$x3y1 $x2y1 $x1y1 $x0y1 \
$x3y0 $x2y0 $x1y0 $x0y0
set modified 0
foreach block {
$BlockOf16 $BlockOf12H $BlockOf09
$BlockOf06H $BlockOf04
} {
set block [expr $block]
if { [info exists MapFixupTable($block)] } {
eval $MapFixupTable($block)
set modified 1
break
}
}
if { !$modified } break
}
}
}
}
if { $DebugLevel > 1 } {
set tmpx ""
for { set y 1 } { $y <= $height } { incr y } {
for { set x 1 } { $x <= $width } { incr x } {
append tmpx $newmap($x:$y)
}
append tmpx "\n"
}
puts "$rtn: final map:"
puts $tmpx
}
set tmp ""
for { set y 1 } { $y <= $height } { incr y } {
for { set x 1 } { $x <= $width } { incr x } {
append tmp $newmap($x:$y)
}
}
return $tmp
}
#---------------------------------------------------------------------
# Routine: setup_background
# Purpose: Sets up the program's "background" layer
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_background {} {
global fr1data
global BGTileWidth BGTileHeight BGWidth BGHeight
global BRICKAPI FRAFMTRGB NRDIGITS
set n1 [string length $fr1data]
set n2 [expr $BGTileWidth * $BGTileHeight * $NRDIGITS]
if { $n1 != $n2 } {
puts "$IE-01" ; exit 1
}
set t1 [br::tile create]
set fr1 [br::frame create $FRAFMTRGB \
$BGTileWidth $BGTileHeight \
[binary format H$n1 $fr1data]]
br::tile add-frame $t1 $fr1
set layer_id [br::layer add]
set layers(bg) $layer_id
if { $BRICKAPI < 5300 } {
set info_list [br::layer info $layer_id]
set layers(bg.spr-list) [lindex $info_list 0]
set layers(bg.map) [lindex $info_list 1]
set layers(bg.str-list) [lindex $info_list 2]
} else {
set layers(bg.spr-list) [br::layer sprite-list $layer_id]
set layers(bg.map) [br::layer map $layer_id]
set layers(bg.str-list) [br::layer string-list $layer_id]
}
br::map tile-size $layers(bg.map) $BGTileWidth $BGTileHeight
br::map tile $layers(bg.map) 1 $t1
br::map size $layers(bg.map) $BGWidth $BGHeight
br::map set-data $layers(bg.map) \
[binary format H[expr {4 * $BGWidth * $BGHeight}] \
[string repeat 0100 [expr {$BGWidth * $BGHeight}]]]
}
#---------------------------------------------------------------------
# Routine: setup_keyboard
# Purpose: Sets up keyboard operations
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_keyboard {} {
# Watch for the "h" key
global KeyH_Input KeyH_Button KeyH_SDLCode
br::io assign $KeyH_Input button $KeyH_Button $KeyH_SDLCode
# Watch for the "i" key
global KeyI_Input KeyI_Button KeyI_SDLCode
br::io assign $KeyI_Input button $KeyI_Button $KeyI_SDLCode
# Watch for the "q" key
global KeyQ_Input KeyQ_Button KeyQ_SDLCode
br::io assign $KeyQ_Input button $KeyQ_Button $KeyQ_SDLCode
# Watch for the space key
global KeySpace_Input \
KeySpace_Button KeySpace_SDLCode
br::io assign $KeySpace_Input button \
$KeySpace_Button $KeySpace_SDLCode
}
#---------------------------------------------------------------------
# Routine: make_proto_sprite
# Purpose: Creates a sprite prototype
# Arguments: Explained below
#---------------------------------------------------------------------
# 1. Usage is straightforward. Use calls similar to the following:
#
# make_proto_sprite square 4 4 1 1 \
# [list $TRANSPARRGB $RED] move {
# ****
# *..*
# ****
# }
# 2. Arguments are:
#
# Sprite-type name. Presently, this should be an object-class name,
# though special cases may arise in the future.
#
# Width in pixels and height in pixels
# Initial X-scale factor
# Initial Y-scale factor
# A list of color values as explained below
# A motion-related argument as explained below
# A sprite drawn as inline text as shown above
# 3. Use "*" to represent a foreground pixel, "." to represent a back-
# ground pixel, and/or digits "0" through "9", lower-case letters "a"
# through "z", and upper-case letters "A" through "Z" to represent
# pixels of up to 62 additional types, for a total of 64 possible col-
# ors.
# The terms "foreground" and "background" are simply convenient lab-
# els. In this context, they have no fixed meaning.
# Restrictions: A digit can't be used in the inline-text drawing un-
# less all preceding digits are also used. A lower-case letter can't
# be used unless all digits and all preceding lower-case letters are
# also used. An upper-case letter can't be used unless all digits, all
# lower-case letters, and all preceding upper-case letters are also
# used.
# The color list should contain a set of six-digit hex color codes.
# The list may be from 2 to 38 elements long. The first element speci-
# fies the background color and the second one specifies the fore-
# ground color. If subsequent elements are present, they specify the
# colors associated with digits "0", "1", "2", etc. through "9",
# "a", "b", "c", etc. through "z", and "A", "B", "C", etc. through
# "Z".
# 4. For a normal mobile sprite, the motion argument should be "move".
# This installs the following Brick Engine motion program:
#
# { add xpos, xvel
# add ypos, yvel }
# For a stationary sprite, the argument should be either "nomove" or
# "stationary", though "move" presently works as well.
# For special cases, pass any desired motion program instead of one
# of these keywords. The motion program should consist of a curly-
# brace block that contains appropriate commands (see the example
# shown above). For more information, see the Brick Engine API docu-
# mentation.
# 5. Two or more "make_proto_sprite" calls may be made for the same
# sprite type. If this is done, the result is a sprite prototype that
# contains multiple frames (one frame per call).
# 6. On exit from a "make_proto_sprite" call, the following global
# variables are set ($name stands for the sprite-type name):
#
# proto($name) # Sprite prototype
#
# gdata($name.num_frames) # Number of frames in sprite (1+)
#
# # Frame index for the first frame that
# # was added to the current sprite pro-
# # totype (subsequent calls don't cha-
# # nge this)
# gdata($name.frame_index.default)
#
# # Frame index for the frame that was
# # just added
# gdata($name.frame_index.newest)
#
# gdata($name.frame_index) # Current frame index (same as default
# # index until higher-level code chang-
# # es it)
#---------------------------------------------------------------------
dmproc 1 make_proto_sprite { \
name width height x_scale y_scale colors move drawing } {
global proto BRICKAPI FRAFMTTRA NRDIGITS TRANSPARRGB
global gdata
# Remove white space and quotes from
# the sprite drawing
regsub -all {[ "'\n\r\t]+} $drawing "" drawing
# This block is a safety measure. It verifies that the dimensions and
# drawing provided are consistent.
set area [expr $width * $height ]
set xlen [string length $drawing ]
if { $area != $xlen } {
puts "$IE-01: $rtn:\nDimensions and drawing\
specified for"
puts "$name are inconsistent:"
puts "Width: $width Height: $height Text: $drawing"
exit 1
}
# This block is a safety measure. It verifies that all elements in
# $colors are integers and converts them to the appropriate number of
# hex digits.
set clen [llength $colors]
for { set ii 0 } { $ii < $clen } { incr ii } {
set color [lindex $colors $ii]
regsub {^0x} $color "" color
set decimal_value [expr 0x$color]
set OPACITY FF
if { $color eq $TRANSPARRGB } { set OPACITY 00 }
if { $BRICKAPI < 5400 } { set OPACITY "" }
set color [format "%0${NRDIGITS}x${OPACITY}" $decimal_value]
set colors [lreplace $colors $ii $ii $color]
}
# Build a character-to-color map
set CharMap [list "." [lindex $colors 0] "*" [lindex $colors 1]]
set lcaval [scan a %c] ; # ASCII value of letter "a"
set ucaval [scan A %c] ; # ASCII value of letter "A"
set NumDigits 10 ; # No. of decimal digits (10)
set NumLCLetters 26 ; # No. of lower-case letters (26)
set NumUCLetters 26 ; # No. of upper-case letters (26)
; # No. of possible color chars below
; # the upper-case letters
set NumCharsBelowUpper [expr $NumDigits + $NumLCLetters]
; # No. of possible color chars (64)
set NumColorChars \
[expr $NumDigits + $NumLCLetters + $NumUCLetters]
for { set ii 1 } { $ii <= $NumColorChars } { incr ii } {
set jj [expr $ii + 1]
set ColorHex [lindex $colors $jj]
if { $ColorHex eq "" } { break }
set ColorChar [expr $ii - 1]
if { $ii > $NumDigits } {
set ColorChar [format %c \
[expr $lcaval + $ii - ($NumDigits + 1)]]
}
if { $ii > $NumCharsBelowUpper } {
set ColorChar [format %c \
[expr $ucaval + $ii - ($NumCharsBelowUpper + 1)]]
}
set CharMap [concat $CharMap $ColorChar $ColorHex]
}
# Create hex version of sprite shape
set drawing_hex [string map $CharMap $drawing]
# Create a frame for the sprite
if { $BRICKAPI < 5400 } {
global CHROMA_R CHROMA_G CHROMA_B
set frame [br::frame create $FRAFMTTRA $width $height \
[binary format H* $drawing_hex] \
$CHROMA_R $CHROMA_G $CHROMA_B]
} else {
set frame [br::frame create $FRAFMTTRA $width $height \
[binary format H* $drawing_hex]]
}
# Flag: New sprite
set is_new [expr [info exists proto($name)] ? 0 : 1]
# Set up the requested prototype
if { $is_new } {
set proto($name) [br::sprite create]
if { $DebugLevel } { puts "proto($name)=$proto($name)" }
if { $BRICKAPI >= 5400 } {
br::sprite scale $proto($name) $x_scale $y_scale
}
br::sprite collides $proto($name) box
set gdata($name.num_frames) 1
} else {
incr gdata($name.num_frames)
}
set frame_index [br::sprite add-frame $proto($name) $frame]
set gdata($name.frame_index.newest) $frame_index
if { ![info exists gdata($name.frame_index)] } {
set gdata($name.frame_index) $frame_index
set gdata($name.frame_index.default) $frame_index
}
set LocalShadows [get_class_param $name dropshadow]
if { ($BRICKAPI >= 5400) && $LocalShadows } {
br::sprite add-subframe $proto($name) \
$frame_index \
[br::frame effect $frame dropshadow 2 2 4 40 40 40]
}
br::sprite bound $proto($name) $frame_index 0 0 $width $height
if { $is_new } {
if { $move eq "move" } {
br::sprite load-program $proto($name) \
{ add xpos, xvel
add ypos, yvel }
} elseif { $move eq "nomove" } {
} elseif { $move eq "stationary" } {
} else {
br::sprite load-program $proto($name) $move"
}
}
return $frame_index
}
#---------------------------------------------------------------------
# Routine: make_proto_ocbullet
# Purpose: Creates a sprite prototype: ocbullet class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocbullet
dmproc 1 make_proto_ocbullet {} {
global BG_BULLET FG_BULLET
make_proto_sprite ocbullet \
1 1 1 1 [list 0 0] move {
*
}
}
#---------------------------------------------------------------------
# Routine: make_proto_occar
# Purpose: Creates a sprite prototype: occar class
# Arguments: None
# Note: The associated sprite should be a small car driving left. See
# "make_proto_ocplayer".
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) occar
dmproc 1 make_proto_occar {} {
global TRANSPARRGB
make_proto_sprite occar \
22 9 1 1 [list $TRANSPARRGB \
3A6D89 A2A2A3 4F778E 4A7C98 8E9EA9 161514 \
035D88 094B71 9F9FA0 445058 233845 87959F \
476472 718693 9AA6AE 3D7594 2E7295 0B4A6D \
1D313D 4F5A62 34617E 01628B 1F6182 34444C \
1F5874 2F4B59 0C3244 628CA0 648699 145C81 \
365063] \
move {
........r*******q.....
........*..**...b*r...
.....3a*c..**...12*r..
..dr1e11222**e222efsq.
.2t9n5555k55555k*9hjl0
cp44n*5555kk5555p44nlj
84ma4msg6666g6gb4m34lo
.4i84m70707000704i84..
..44.............44...
}
}
#---------------------------------------------------------------------
# Routine: make_proto_occow
# Purpose: Creates a sprite prototype: occow class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) occow
dmproc 1 make_proto_occow {} {
global TRANSPARRGB
make_proto_sprite occow \
12 7 1 1 [list $TRANSPARRGB 000000 FFFFFF 808080] \
move {
*1..........
.*..........
*0********1.
****00***.*1
..*0**00*..*
..*******...
..*.....*...
}
}
#---------------------------------------------------------------------
# Routine: make_proto_occross
# Purpose: Creates a sprite prototype: occross class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) occross
dmproc 1 make_proto_occross {} {
global TRANSPARRGB
make_proto_sprite occross \
8 13 1 1 [list $TRANSPARRGB CCCC00] move {
...**...
...**...
...**...
********
********
...**...
...**...
...**...
...**...
...**...
...**...
...**...
...**...
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ocdog
# Purpose: Creates a sprite prototype: ocdog class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocdog
dmproc 1 make_proto_ocdog {} {
global TRANSPARRGB
make_proto_sprite ocdog \
17 13 1 1 [list $TRANSPARRGB \
FFFFFF D2A394 C68A79 AA0000 E8CAC1 FFEFE8 \
FFEBE4 F1CFC4 EDCFC6 CB9C8D C39B8E BB9488 \
AD796A D3AEA3 FFFAF8 2B0300 C09183 A43C1D \
8A4A36 FAE0D9 B88879 AF877A D8B4AA E0BCB2 \
C79F94 8E4E3A 000000] \
move {
.....pppp........
.....pppp........
pp.....ppooo.....
ppooooo189nbo....
ppkalf3pp**5joppp
oi5**d*po***4oppp
o*********pp*oepp
o*********po*o.pp
o************o.pp
ocm0*********o...
.22h********7o...
.22hooo****6oo...
.22h..oooogoo....
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ocflames
# Purpose: Creates a sprite prototype: ocflames class
# Arguments: None
#---------------------------------------------------------------------
# This is a multi-frame sprite. The sprite's frames are used for anim-
# ation, in this case, as opposed to multiple shapes.
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocflames
dmproc 1 make_proto_ocflames {} {
global TRANSPARRGB
# Frame #1, index 0
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 800000 808000 FFFF00 CCCCCC FFFFFF \
000000] \
move {
.............................................
.............................................
.......000...................................
......0000..........0..................0.....
.000000000....0...0000............000.000....
0000000*00...00000000..........00000000000.00
000000*000..000000000..........00000000000000
000000000000000000000.........000000000000000
0000**000000000000000...0000.0000000000000000
000***00000*000000000000000000000000000*00000
00****000****0000**000000000000*0000000*00000
00****000******0***00**0000000**0000000**0000
******************000**0000000**000000***000*
******************00***00***00***0**00**0000*
******************00*********0******00*00000*
***********************************000000*00*
*****************0*************1*****0****00*
*****221*********0*********1***********22*00*
*****222****************1*22***2******222****
*0****22*22***12**21**22222***2221***2221****
*0***122*22*2222*121*222222**2222***22221*212
1*****2222222222*222222122**12222***2222*1*22
******2222222222*222222*22*212222*222221***22
2122222222**122*222322212**222221222222***2**
2222224222***222322222222*1*2*22**2222221*222
22*2224*22***22232222*22*20*024***222223*1222
2214*24*42*004114222*0*25350*34*22312***02222
}
# Frame #2, index 1
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 800000 808000 FFFF00 CCCCCC FFFFFF \
000000] \
move {
.............................................
.............................................
.............................................
..............................0..............
...0000...000.....00.........000.............
..000000.00000...0000........000000..000.....
.0000000.0**00..00000.00....000000000000.....
0000000000**00.00*0000000...00000000000000000
0*0000000***000**00000000.00000**00000*000000
**0000000***000**0000000000000***00000*000***
**0000000**0000*0000000000000****00000*0*****
*00000000**000**0000000*00*******00000*******
*000*0000*000**0000000***********0050********
000***000*00***000000**0*********0000********
00****00*********000***0**********00******000
00****0**********000***0*****************000*
***************2*00****0*********1**2****00**
**************22*0*******2*******2**2***00***
**************22*0*******2******22221****0***
*******1******22***2*****1***12*2222****0**0*
*******21***2122**12*****12*222**22***2*0****
2******22**2222***22222**222222**22***2***2**
***11*222*22222*222222222122221**222*22**12**
22*222222*232*1222222222222222*222222222*222*
222222*222321*222332232*222222*222222222*3222
*1222*2*224***22242234*0222*222212*12*322232*
**22222*213*2***24*32300223*2*2201*22*4442420
}
# Frame #3, index 2
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 800000 808000 FFFF00 CCCCCC FFFFFF \
000000] \
move {
.............................................
.........0.....0.............................
.......000.....00..................0.........
.......000.....00............................
..0000000.....000......00........0...........
.00000000....0000....0000...000000...........
000005000.00.0000..000000..0000000.......0...
000005500.0000000.0000**0000000000..0..000000
0**05500000000**0000****0000000**000000000000
0*005500000000***000***0000*00***000000000000
0000550000000****00****0000*********000000000
0005500000000****00***0000**********000000000
000000000000**********0000**********000000000
***0000000************0*******0*****000*00000
****000000********************00****00***000*
*1**00000****************0000*0000***0****0**
******000****************000****00***********
******00*2******0***************0************
******00*2**22**************22***********1***
*****0*0***222*221*2*12**2222*******1***12***
222**0*0***222222*12*22*2222***221**22*122**1
222*0**000*232232*21*2222222**1222**22*221122
222**1*00*0*32222*212222222**22222*222222*222
222*2210**0*222222*12222121*22222*2122222221*
*21*212****22*2*2202222**2*232221*22*2*22*211
*2*23222*2222*3*22*2222****22*12*2232**12*222
2*2222232232212*22222*222*2222*2*04235*212242
}
# Frame #4, index 3
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 800000 808000 FFFF00 CCCCCC FFFFFF \
000000] \
move {
.............................................
.............................................
.............................................
.............................................
..00.....000............000..............0...
.000...0000000.........00000.......00000000..
0000000000000000......000*00.....000000000000
000000000*0000000....0000*00...00000000000000
0000000***000*0000..000000050000*00**00000000
0000000***00**0000000000000000*******00**0000
0000000***00**0000000*0000000********00**0000
0***000***0***000**00*000000*************0000
****00********000**00000000**************000*
****00*************00**000****************0**
****00*************00*********************0**
******************00**********************0**
*00**************00**************************
*00******************************************
*0*1****0****1*******************************
**22***00***22***1******11********22*****2***
122222*0****22*221**********0****222****22***
222222**2***21*221**********0**2*22*****22**2
***22******122222221*1***1*1***2122***2*22*22
**12*2****222232*222*2222*22***2222**22*222**
*222*2***232222*222*2222222**1224*222220*3221
2*34*21*124222*222222122222**2*22*332220*2222
*242*22*32222**22*322*21****02**124222**2222*
}
# Frame #5, index 4
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 800000 808000 FFFF00 CCCCCC FFFFFF \
000000] \
move {
.............................................
.............................................
.............................................
.................0000.........0..............
.......0000......00000.......00000...........
00000..00000....000000......0000000..000.0000
000000000000...0000000....000000000.000000000
0000000000000..00000000..00000000000000000000
0000000000000.000000000.000000000000000000000
0000**0000*0000*000*0000000000000000**0000550
00*****00**000**00**000000000000000****000000
00*****00*******00**000000000000000****000000
******00********0*****00000**00000****000000*
******0*********0*****0000***00*******00*000*
******0***************000****0*******000*00**
0************************************00******
**********12************1********************
******221*11*****211***221***11********22****
******2*******1*12221**111**122********22****
121**22*******222222222222**222*************1
*2**122***0***221222222222*222****1*1********
*2*222*******12222*2222*22*222**222*2*****2**
*22222*12**122222**2222*2*2222*221**22***22**
222222**22***12220**22222*222222****22*1**222
222*22*222***122**21222212222222*22222222*222
2***22*22*22*2***22*2*2222223****2222421222**
222222*222222****2**00224*224222323*4323*22**
}
# Frame #6, index 5
make_proto_sprite ocflames \
45 27 1 1 [list $TRANSPARRGB \
FF0000 808000 800000 FFFFFF CCCCCC FFFF00] \
move {
.............................................
.............................................
.............................................
.............................................
.11.............111111...1111................
11...111.......11111111.11111..111.1.........
11..11111....1111111111111111.1111111......11
11..11111..11111111111111111111111111.....111
111111111.1111111111111***1111**111111...1111
111111111.1111111111111***1111**11111111.1111
111111111111111*1111111**11111*111*11111.1111
11111111111111*11111111**11111*1****111111111
11*****11111***11111111**111*11*****11111*111
11*******111***11*1111***11**11******1*1**111
1*********1****1***111***11**11***********111
*******************111***1**111***********111
***************1****1*******11*****0******11*
******40***************************4**0******
******44****************4*********0444444****
0*****44*4*****4***440**4*********04444444**0
4**0**4044*0*1*44*444**44***********444444**4
4*44*0444004*1*404444**44*4***44****0444***44
40444444**4***0**4*4***4**4***440****440**444
443444**04****4*44*4*****44**04444****04*44**
*4244***4**4444*4**4*4*0*4*40**444*****0*****
*44404444*4444*43**4440*440*4***43*1*04*41*4*
44444040444244**24*0314*4*4*444144*41*424134*
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ocinter
# Purpose: Creates a sprite prototype: ocinter class
# Arguments: None
#---------------------------------------------------------------------
# This is a multi-frame sprite. The frames are used as shapes as op-
# posed to animation. When the class is instantiated, individual shap-
# es may be selected explicitly as follows:
#
# set frame_index $gdata(CLASSNAME.frame_index.SHAPENAME)
#
# where CLASSNAME is the object-class name (ocinter) and SHAPENAME is
# a shape keyword (forward or reverse).
# Alternatively, a shape may be selected randomly:
#
# set frame_index \
# [random_int 0 [expr $gdata(CLASSNAME.num_frames) - 1]]
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocinter
dmproc 1 make_proto_ocinter {} {
global gdata
global BG_PORTAL_FORWARD FG_PORTAL_FORWARD
global BG_PORTAL_REVERSE FG_PORTAL_REVERSE
# Inter-world "forward" portal
make_proto_sprite ocinter \
5 5 1 1 [list $BG_PORTAL_FORWARD $FG_PORTAL_FORWARD] \
nomove {
.*...
..*..
...*.
..*..
.*...
}
# Make "forward" shape addressable
set gdata(ocinter.frame_index.forward) \
$gdata(ocinter.frame_index.newest)
# Inter-world "reverse" portal
make_proto_sprite ocinter \
5 5 1 1 [list $BG_PORTAL_REVERSE $FG_PORTAL_REVERSE] \
nomove {
...*.
..*..
.*...
..*..
...*.
}
# Make "reverse" shape addressable
set gdata(ocinter.frame_index.reverse) \
$gdata(ocinter.frame_index.newest)
}
#---------------------------------------------------------------------
# Routine: make_proto_ocintra
# Purpose: Creates a sprite prototype: ocintra class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocintra
dmproc 1 make_proto_ocintra {} {
global BG_PORTAL_INTRA FG_PORTAL_INTRA
make_proto_sprite ocintra \
4 4 1 1 [list $BG_PORTAL_INTRA $FG_PORTAL_INTRA] nomove {
****
*..*
*..*
****
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ockarkinos
# Purpose: Creates a sprite prototype: ockarkinos class
# Arguments: None
#---------------------------------------------------------------------
# This is a multi-frame sprite. The frames are used as shapes as op-
# posed to animation. When the class is instantiated, individual shap-
# es may be selected explicitly as follows:
#
# set frame_index $gdata(CLASSNAME.frame_index.SHAPENAME)
#
# where CLASSNAME is the object-class name (ockarkinos) and SHAPENAME
# is a shape keyword (small, medium, large).
# Alternatively, a shape may be selected randomly:
#
# set frame_index \
# [random_int 0 [expr $gdata(CLASSNAME.num_frames) - 1]]
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ockarkinos
dmproc 1 make_proto_ockarkinos {} {
global BG_KARKINOS FG_KARKINOS TRANSPARRGB gdata
# Start with a small version
make_proto_sprite ockarkinos \
3 3 1 1 [list $BG_KARKINOS $FG_KARKINOS] move {
*.*
...
***
}
# Make "small" shape addressable
set gdata(ockarkinos.frame_index.small) \
$gdata(ockarkinos.frame_index.newest)
# Add a medium-size version
make_proto_sprite ockarkinos \
8 8 1 1 [list $BG_KARKINOS $FG_KARKINOS] move {
**....**
**....**
........
........
........
........
........
********
}
# Make "medium" shape addressable
set gdata(ockarkinos.frame_index.medium) \
$gdata(ockarkinos.frame_index.newest)
# Add a large version
make_proto_sprite ockarkinos \
20 20 1 1 [list $BG_KARKINOS $FG_KARKINOS] move {
***..............***
***..............***
***..............***
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
....................
********************
********************
}
# Make "large" shape addressable
set gdata(ockarkinos.frame_index.large) \
$gdata(ockarkinos.frame_index.newest)
}
#---------------------------------------------------------------------
# Routine: make_proto_ocmedical
# Purpose: Creates a sprite prototype: ocmedical class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocmedical
dmproc 1 make_proto_ocmedical {} {
global BG_MEDICAL FG_MEDICAL
make_proto_sprite ocmedical \
2 2 1 1 [list $BG_MEDICAL $FG_MEDICAL] move {
**
**
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ocmoney
# Purpose: Creates a sprite prototype: ocmoney class
# Arguments: None
#---------------------------------------------------------------------
# This is a multi-frame sprite. The frames are used as shapes as op-
# posed to animation. When the class is instantiated, individual shap-
# es may be selected explicitly as follows:
#
# set frame_index $gdata(CLASSNAME.frame_index.SHAPENAME)
#
# where CLASSNAME is the object-class name (ocmoney) and SHAPENAME is
# a shape keyword (dollar, cent, pound, yen).
# Alternatively, a shape may be selected randomly:
#
# set frame_index \
# [random_int 0 [expr $gdata(CLASSNAME.num_frames) - 1]]
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocmoney
dmproc 1 make_proto_ocmoney {} {
global gdata TRANSPARRGB
# Start with the U.S. dollar
make_proto_sprite ocmoney \
5 7 1 1 [list $TRANSPARRGB 008800] \
move {
..*..
*****
*.*..
*****
..*.*
*****
..*..
}
# Make "dollar" shape addressable
set gdata(ocmoney.frame_index.dollar) \
$gdata(ocmoney.frame_index.newest)
# Alternate: U.S. cent
make_proto_sprite ocmoney \
5 7 1 1 [list $TRANSPARRGB 008800] \
move {
..*..
*****
*.*..
*.*..
*.*..
*****
..*..
}
# Make "cent" shape addressable
set gdata(ocmoney.frame_index.cent) \
$gdata(ocmoney.frame_index.newest)
# Alternate: British pound
make_proto_sprite ocmoney \
5 7 1 1 [list $TRANSPARRGB 008800] \
move {
.****
.*..*
.*...
****.
.*...
.*..*
*****
}
# Make "pound" shape addressable
set gdata(ocmoney.frame_index.pound) \
$gdata(ocmoney.frame_index.newest)
# Alternate: Japanese yen
make_proto_sprite ocmoney \
5 7 1 1 [list $TRANSPARRGB 008800] \
move {
*...*
.*.*.
..*..
*****
..*..
*****
..*..
}
# Make "yen" shape addressable
set gdata(ocmoney.frame_index.yen) \
$gdata(ocmoney.frame_index.newest)
}
#---------------------------------------------------------------------
# Routine: make_proto_ocpig
# Purpose: Creates a sprite prototype: ocpig class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocpig
dmproc 1 make_proto_ocpig {} {
global TRANSPARRGB
make_proto_sprite ocpig \
12 13 1 1 [list $TRANSPARRGB \
F0BEBE B18A89 A0878A 918486 775D5F E4A6B2 \
BE8F92 CC9E96 D6A9B4 715F5E B79A9A C89C9F \
FFCACD 999192 442622 AC9699 372023 F9BEBD \
D09E9E DCB5B2 FFFFFF DB6C6D] \
move {
...ffff.ffff
...f2c..f2c.
...fdce1cfc.
888776g6b3..
******ff*ff.
******9fi9f.
*********kkk
***5****4fkf
***0*****fkf
***ah*8ii8..
******8jj8..
***2223888..
***2........
}
}
#---------------------------------------------------------------------
# Routine: make_proto_ocplayer
# Purpose: Creates a sprite prototype: ocplayer class
# Arguments: None
#---------------------------------------------------------------------
# This is a multi-frame sprite. The frames are used as shapes as op-
# posed to animation. When the class is instantiated, individual shap-
# es may be selected explicitly as follows:
#
# set frame_index $gdata(CLASSNAME.frame_index.SHAPENAME)
#
# where CLASSNAME is the object-class name (ocplayer) and SHAPENAME is
# a shape keyword (normal, godmode, driving_left, or driving_right).
# Alternatively, a shape may be selected randomly:
#
# set frame_index \
# [random_int 0 [expr $gdata(CLASSNAME.num_frames) - 1]]
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocplayer
dmproc 1 make_proto_ocplayer {} {
global BG_PLAYER FG_PLAYER TRANSPARRGB gdata
# Normal player shape
make_proto_sprite ocplayer \
6 6 1 1 [list $BG_PLAYER $FG_PLAYER] move {
**..**
**..**
......
......
******
******
}
# Make "normal" shape addressable
set gdata(ocplayer.frame_index.normal) \
$gdata(ocplayer.frame_index.newest)
# Player in "God" mode
make_proto_sprite ocplayer \
6 6 1 1 [list FFFF00 8800FF] move {
**..**
**..**
......
......
******
******
}
# Make "God mode" shape addressable
set gdata(ocplayer.frame_index.godmode) \
$gdata(ocplayer.frame_index.newest)
# Player driving a car left (sprite
# should be the same here as for an
# occar)
make_proto_sprite ocplayer \
22 9 1 1 [list $TRANSPARRGB \
3A6D89 A2A2A3 4F778E 4A7C98 8E9EA9 161514 \
035D88 094B71 9F9FA0 445058 233845 87959F \
476472 718693 9AA6AE 3D7594 2E7295 0B4A6D \
1D313D 4F5A62 34617E 01628B 1F6182 34444C \
1F5874 2F4B59 0C3244 628CA0 648699 145C81 \
365063] \
move {
........r*******q.....
........*..**...b*r...
.....3a*c..**...12*r..
..dr1e11222**e222efsq.
.2t9n5555k55555k*9hjl0
cp44n*5555kk5555p44nlj
84ma4msg6666g6gb4m34lo
.4i84m70707000704i84..
..44.............44...
}
# Make "driving left" shape address-
# able
set gdata(ocplayer.frame_index.driving_left) \
$gdata(ocplayer.frame_index.newest)
# Player driving a car facing right
# (should be the previous shape re-
# versed)
make_proto_sprite ocplayer \
22 9 1 1 [list $TRANSPARRGB \
3A6D89 A2A2A3 4F778E 4A7C98 8E9EA9 161514 \
035D88 094B71 9F9FA0 445058 233845 87959F \
476472 718693 9AA6AE 3D7594 2E7295 0B4A6D \
1D313D 4F5A62 34617E 01628B 1F6182 34444C \
1F5874 2F4B59 0C3244 628CA0 648699 145C81 \
365063] \
move {
.....q*******r........
...r*b...**..*........
..r*21...**..c*a3.....
.qsfe222e**22211e1rd..
0ljh9*k55555k5555n9t2.
jln44p5555kk5555*n44pc
ol43m4bg6g6666gsm4am48
..48i40700070707m48i4.
...44.............44..
}
# Make "driving right" shape address-
# able
set gdata(ocplayer.frame_index.driving_right) \
$gdata(ocplayer.frame_index.newest)
}
#---------------------------------------------------------------------
# Routine: make_proto_ocscroll
# Purpose: Creates a sprite prototype: ocscroll class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) ocscroll
dmproc 1 make_proto_ocscroll {} {
global BG_SCROLL FG_SCROLL
make_proto_sprite ocscroll \
5 5 1 1 [list $BG_SCROLL $FG_SCROLL FFFFFF] \
nomove {
*****
.*0*.
.*0*.
.*0*.
*****
}
}
#---------------------------------------------------------------------
# Routine: make_proto_octiger
# Purpose: Creates a sprite prototype: octiger class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) octiger
dmproc 1 make_proto_octiger {} {
global TRANSPARRGB
make_proto_sprite octiger \
12 12 1 1 [list $TRANSPARRGB \
F6FBDB A34F07 8E7354 DA7117 5E4F3F 794718 \
FF8919 8F531C 6B4D2F A98C65 DF9F58 824813 \
E6710C A09782 151615 332F24 EFF4D4 E7E9CD \
2E1A0A 240C00 DF6E0E 603E1B EEEED6 904E11 \
FFDDA1 4B4643 909285 AF580D 84867B C8CCB3 \
543212] \
move {
22........22
261666666162
.43t0kak0o34
.b9f*qaq**9b
.bne*j5j*enb
.b9**m5m**9b
.bcs*ith*scb
.bs*fpdp**gb
.1b***rlf*b1
..bg81718gb.
...g5bbb5g..
...g.111.g..
}
}
#---------------------------------------------------------------------
# Routine: make_proto_octree
# Purpose: Creates a sprite prototype: octree class
# Arguments: None
#---------------------------------------------------------------------
lappend gdata(list_classes_proto) octree
dmproc 1 make_proto_octree {} {
global TRANSPARRGB
make_proto_sprite octree \
12 12 1 1 [list $TRANSPARRGB \
00AA35 606000 00B83A 00842B 7F7F00 007826 \
00C43E 00952F 00CD42 656500 898A00 18AB2F \
2CA626 009E32 006C2B] \
move {
...22**777..
...***1777..
...*111*157.
..5*11642*7.
..5**1*6*57.
..**11*6c**.
...2*ba2d25.
....999d2...
....999.....
....999.....
...99399....
...999880...
}
}
#---------------------------------------------------------------------
# Routine: setup_sprite_prototypes
# Purpose: Sets up sprite prototypes
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_sprite_prototypes {} {
global gdata
foreach objclass $gdata(list_classes_proto) {
set cmd make_proto_$objclass
eval $cmd
}
}
#---------------------------------------------------------------------
# Routine: set_world_name
# Purpose: Set current world name
# Arguments: name = World name
# When a new world is activated, this routine is called to record the
# change.
#---------------------------------------------------------------------
dmproc 1 set_world_name { name } {
global gdata lv
# Note: We'd like to get rid of gdata(lv) and use lv exclusively, but
# this would require changes to the game's "trace" code.
set lv $name
set gdata(lv) $name
}
#---------------------------------------------------------------------
# Routine: track_sprite
# Purpose: Camera-related utility routine
# Arguments: xpos = X-position (in sprite coordinate system)
# ypos = Y-position (in sprite coordinate system)
# This routine centers the program's virtual camera on the specified
# position.
#---------------------------------------------------------------------
dmproc 5 track_sprite { xpos ypos } {
global GAME_WIDTH GAME_HEIGHT
global layers lv
set WX [expr int ($GAME_WIDTH / 2) ]
set HX [expr int ($GAME_HEIGHT / 2) ]
br::layer camera $layers($lv) \
[expr { $xpos - $WX }] [expr { $ypos - $HX }]
}
#---------------------------------------------------------------------
# Routine: account_ocplayer_position
# Purpose: Player-related utility routine
# Arguments: None
# This routine updates various records based on the ocplayer sprite's
# current position. It also repositions the program's virtual camera
# based on the position in question.
#---------------------------------------------------------------------
dmproc 5 account_ocplayer_position {} {
global gdata layers lv sdata
set id $gdata($lv,ocplayer_id)
set PlayerPosition [br::sprite pos $id]
set ocplayer_x [lindex $PlayerPosition 0]
set ocplayer_y [lindex $PlayerPosition 1]
# Update ocplayer's internal coordin-
# ates
set sdata($id.px) $ocplayer_x
set sdata($id.py) $ocplayer_y
# Track ocplayer with camera
track_sprite $ocplayer_x $ocplayer_y
}
#---------------------------------------------------------------------
# Routine: (xproc) check_force_create
# Purpose: Checks a global flag
# Arguments: FlagCreate = Output flag (passed by reference)
# This is an "xproc" routine; i.e., it supports "&variable"-style
# pass-by-reference.
# If the following flag exists and is true, this routine sets the spe-
# cified output variable to true (in the caller's scope):
#
# gdata(force_create)
#
# Otherwise, the output flag isn't modified. This routine doesn't re-
# turn anything (except through the output flag).
#---------------------------------------------------------------------
xproc check_force_create { &FlagCreate } {
global gdata
if { [info exists gdata(force_create)] && \
$gdata(force_create) } { set FlagCreate 1 }
set gdata(force_create) 0
}
#---------------------------------------------------------------------
# Routine: group_generic_new
# Purpose: Object creation support routine
# Arguments: objclass = Object class (for example, occow)
# This is a generic object creation routine suitable for "barnyard"-
# group classes and, if class parameters are set appropriate, for some
# of the other classes.
#---------------------------------------------------------------------
dmproc 2 group_generic_new { objclass } {
global layers lv proto sdata ParamBlock WorldKeyStart
global BRICKAPI
# Get class parameters
foreach param [list \
divmax divmin forceposn frequency \
heffect maxnum nobounce scalemin \
scalemax shoot_can shoot_effect shoot_score \
xpos ypos] {
set $param [get_class_param $objclass $param]
}
# Select a scale factor
set scale_factor [random_real $scalemin $scalemax]
if { $scale_factor < 0.75 } { set scale_factor 1.00 }
# Select a speed divisor
set speed_divisor [random_int $divmin $divmax]
if { $speed_divisor < 1 } { set speed_divisor 1 }
# Determine whether or not to create a
# new instance
set FlagCreate 0
if { rand() >= (1 - $frequency) } { set FlagCreate 1 }
check_force_create FlagCreate
if { [get_class_counter $objclass] >= $maxnum } \
{ set FlagCreate 0 }
# Create a new instance?
if { $FlagCreate } { ; # Yes
set id [br::sprite copy $proto($objclass)]
incr_class_counter $objclass
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
set zhint [get_class_param $objclass zhint]
br::sprite z-hint $id $zhint
# Scale the sprite if appropriate
if { ($BRICKAPI >= 5400) && ($scale_factor != 1) } {
br::sprite scale $id $scale_factor $scale_factor
}
# Select object name
set name [get_object_name_random $objclass]
array set sdata [list \
$id. run_$objclass \
$id.ct 0 \
$id.dir [random_direction] \
$id.forceposn $forceposn \
$id.heffect $heffect \
$id.name $name \
$id.nobounce $nobounce \
$id.shoot_can $shoot_can \
$id.shoot_effect $shoot_effect \
$id.shoot_score $shoot_score \
$id.smart 1 \
$id.speed_divisor $speed_divisor \
$id.xpos $xpos \
$id.ypos $ypos \
]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
return 0
}
#---------------------------------------------------------------------
# Routine: new_occar
# Purpose: Objection creation: occar class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_occar {} {
return [group_generic_new occar]
}
#---------------------------------------------------------------------
# Routine: new_occow
# Purpose: Objection creation: occow class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_occow {} {
return [group_generic_new occow]
}
#---------------------------------------------------------------------
# Routine: new_occross
# Purpose: Objection creation: occross class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_occross {} {
return [group_generic_new occross]
}
#---------------------------------------------------------------------
# Routine: new_ocdog
# Purpose: Objection creation: ocdog class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocdog {} {
return [group_generic_new ocdog]
}
#---------------------------------------------------------------------
# Routine: new_ocflames
# Purpose: Object creation: ocflames class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocflames {} {
return [group_generic_new ocflames]
}
#---------------------------------------------------------------------
# Routine: new_ocinter
# Purpose: Objection creation: ocinter class
#
# Arguments: PortalType = "forward" or "reverse".
#
# ToWorldName = Name of the destination world.
#
# ToPortalID = If $PortalType is "reverse", $ToPortalID
# specifies the sprite ID of the destination portal.
# Otherwise, this argument should be "none".
# For an explanation of inter-world portals, see the documentation
# section named "Inter-world portals".
# This routine creates one inter-world portal of the specified type.
# It returns the associated sprite ID. Note: This routine should only
# be used by "make_world" and "run_ocinter".
#---------------------------------------------------------------------
dmproc 2 new_ocinter { PortalType ToWorldName ToPortalID } {
global lv gdata proto sdata layers
set id [br::sprite copy $proto(ocinter)]
set frame_index $gdata(ocinter.frame_index.$PortalType)
br::sprite frame $id $frame_index
if { $DebugLevel >= 2 } {
puts "$rtn: $lv,$id $ToWorldName,$ToPortalID"
}
array set sdata [list $id. run_ocinter \
$id.to_world $ToWorldName \
$id.to_portal $ToPortalID \
]
verify_sprite_exists new_ocinter $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
#---------------------------------------------------------------------
# Routine: new_ocintra
# Purpose: Objection creation: ocintra class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocintra {} {
global lv proto sdata layers
set id [br::sprite copy $proto(ocintra)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
set zhint [get_class_param ocintra zhint]
br::sprite z-hint $id $zhint
array set sdata [list $id. run_ocintra]
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
#---------------------------------------------------------------------
# Routine: new_ockarkinos
# Purpose: Objection creation: ockarkinos class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ockarkinos {} {
global gdata layers lv proto sdata
# Get class parameters
foreach param [list \
divmax divmin frequency heffect \
maxnum shoot_can shoot_effect shoot_score \
size smartmax smartmin zhint] {
set $param [get_class_param ockarkinos $param]
}
# Select smart percentage
set smart_percent [random_int $smartmin $smartmax ]
if { $smart_percent > 100 } { set smart_percent 100 }
# Select speed divisor
set speed_divisor [random_int $divmin $divmax ]
if { $speed_divisor < 1 } { set speed_divisor 1 }
# Determine whether or not to create a
# new instance
set FlagCreate 0
if { rand() >= (1 - $frequency) } { set FlagCreate 1 }
check_force_create FlagCreate
if { [get_class_counter ockarkinos] >= $maxnum } \
{ set FlagCreate 0 }
# Create a new instance?
if { $FlagCreate } { ; # Yes
set id [br::sprite copy $proto(ockarkinos)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
br::sprite z-hint $id $zhint
# Select appropriate frame
if { ($size ne "medium") && ($size ne "large") } \
{ set size small }
set frame_index $gdata(ockarkinos.frame_index.$size)
br::sprite frame $id $frame_index
incr_class_counter ockarkinos
# $smart: 1 for a hunter and 0 for a
# grazer
set smart \
[ expr (int (rand() * 101) < $smart_percent) ? 1 : 0 ]
# Select name
set name [get_object_name_random ockarkinos]
array set sdata [list \
$id. run_ockarkinos \
$id.ct 0 \
$id.dir [random_direction] \
$id.heffect $heffect \
$id.name $name \
$id.shoot_can $shoot_can \
$id.shoot_effect $shoot_effect \
$id.shoot_score $shoot_score \
$id.smart $smart \
$id.speed_divisor $speed_divisor \
]
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
}
return 0
}
#---------------------------------------------------------------------
# Routine: new_ocmedical
# Purpose: Objection creation: ocmedical class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocmedical {} {
global gdata layers lv proto sdata
set objclass ocmedical ; # Object class
# Get class parameters
foreach param [list \
cautious divmax divmin frequency \
maxnum shoot_can shoot_effect shoot_score \
zhint] {
set $param [get_class_param $objclass $param]
}
# Select a speed divisor
set speed_divisor [random_int $divmin $divmax]
if { $speed_divisor < 1 } { set speed_divisor 1 }
# Determine whether or not to create a
# new instance
set FlagCreate 0
if { rand() >= (1 - $frequency) } { set FlagCreate 1 }
check_force_create FlagCreate
if { [get_class_counter $objclass] >= $maxnum } { set FlagCreate 0 }
# Create a new instance?
if { $FlagCreate } { ; # Yes
set id [br::sprite copy $proto($objclass)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
incr_class_counter $objclass
br::sprite z-hint $id $zhint
# Select object name
set name [get_object_name_random $objclass]
array set sdata [list \
$id. run_$objclass \
$id.cautious $cautious \
$id.ct 0 \
$id.dir [random_direction] \
$id.name $name \
$id.shoot_can $shoot_can \
$id.shoot_effect $shoot_effect \
$id.shoot_score $shoot_score \
$id.smart 1 \
$id.speed_divisor $speed_divisor \
]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
return 0
}
#---------------------------------------------------------------------
# Routine: new_ocmoney
# Purpose: Object creation: ocmoney class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocmoney {} {
global gdata lv proto sdata layers
set objclass ocmoney ; # Object class
; # Get class parameters
foreach param [list \
cautious divmax divmin shoot_can \
shoot_effect shoot_score smartmax smartmin \
valmax valmin zhint] {
set $param [get_class_param $objclass $param]
}
# Select a value
set value [random_int $valmin $valmax]
# This simplifies text output else-
# where
if { $value < 2 } { set value 2 }
set id [br::sprite copy $proto($objclass)]
# Set money shape displayed to a ran-
# dom shape selected from all money
# frames
set n [random_int 0 [expr $gdata(ocmoney.num_frames) - 1]]
br::sprite frame $id $n
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
incr_class_counter $objclass
br::sprite z-hint $id $zhint
# Is this object smart?
set smart_percent [random_int $smartmin $smartmax ]
if { $smart_percent > 100 } { set smart_percent 100 }
set smart \
[ expr (int (rand() * 101) < $smart_percent) ? 1 : 0 ]
# Select speed divisor
set speed_divisor [random_int $divmin $divmax]
if { $speed_divisor < 1 } { set speed_divisor 1 }
# Select object name
set name [get_object_name_random $objclass]
array set sdata [list \
$id. run_$objclass \
$id.cautious $cautious \
$id.ct 0 \
$id.dir [random_direction] \
$id.name $name \
$id.shoot_can $shoot_can \
$id.shoot_effect $shoot_effect \
$id.shoot_score $shoot_score \
$id.smart $smart \
$id.speed_divisor $speed_divisor \
$id.value $value \
]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
#---------------------------------------------------------------------
# Routine: new_ocpig
# Purpose: Objection creation: ocpig class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocpig {} {
return [group_generic_new ocpig]
}
#---------------------------------------------------------------------
# Routine: new_ocplayer
# Purpose: Objection creation: ocplayer class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocplayer {} {
global gdata layers lv proto sdata
set ocplayer [br::sprite copy $proto(ocplayer)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$ocplayer" }
set gdata($lv,ocplayer_id) $ocplayer
array set sdata [list $ocplayer. run_ocplayer \
$ocplayer.px 10 $ocplayer.py 10 \
$ocplayer.gx 0 $ocplayer.gy 0 \
$ocplayer.shot 0 \
]
br::list add $layers($lv.spr-list) $ocplayer
random_position_sprite $ocplayer
account_ocplayer_position
return $ocplayer
}
#---------------------------------------------------------------------
# Routine: new_ocscroll
# Purpose: Object creation: ocscroll class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_ocscroll {} {
global layers lv proto sdata
set id [br::sprite copy $proto(ocscroll)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
set zhint [get_class_param ocscroll zhint]
br::sprite z-hint $id $zhint
array set sdata [list $id. run_ocscroll]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
#---------------------------------------------------------------------
# Routine: new_octiger
# Purpose: Creates an object: octiger class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_octiger {} {
if { $DebugLevel > 1 } { puts "$rtn" }
global layers lv proto sdata ParamBlock WorldKeyStart
set objclass octiger ; # Object class
# Get class parameters
foreach param [list \
divmax divmin health heffect maxnum \
shoot_can shoot_effect shoot_score sound_destroy \
sound_hit zhint] {
set $param [get_class_param $objclass $param]
}
# Select a speed divisor
set speed_divisor [random_int $divmin $divmax]
if { $speed_divisor < 1 } { set speed_divisor 1 }
set FlagCreate 0 ; # Determine whether or not to create a
; # new instance
if { [get_class_counter $objclass] < $maxnum } {
set FlagCreate 1
}
check_force_create FlagCreate
# Create a new instance?
if { $FlagCreate } { ; # Yes
set id [br::sprite copy $proto($objclass)]
incr_class_counter $objclass
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
br::sprite z-hint $id $zhint
# Select object name
set name [get_object_name_random $objclass]
array set sdata [list \
$id. run_$objclass \
$id.ct 0 \
$id.dir [random_direction] \
$id.health $health \
$id.heffect $heffect \
$id.name $name \
$id.shoot_can $shoot_can \
$id.shoot_effect $shoot_effect \
$id.shoot_score $shoot_score \
$id.smart 1 \
$id.sound_destroy $sound_destroy \
$id.sound_hit $sound_hit \
$id.speed_divisor $speed_divisor \
]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
return 0
}
#---------------------------------------------------------------------
# Routine: new_octree
# Purpose: Object creation: octree class
# Arguments: None
#---------------------------------------------------------------------
dmproc 2 new_octree {} {
global layers lv proto sdata ParamBlock WorldKeyStart
set id [br::sprite copy $proto(octree)]
if { $DebugLevel >= 2 } { puts "$rtn: $lv,$id" }
set zhint [get_class_param octree zhint]
br::sprite z-hint $id $zhint
set add_octiger 0
if { ![get_world_param has_octiger] } {
set add_octiger 1
set_world_param has_octiger 1
}
array set sdata [list \
$id. run_octree \
$id.add_octiger $add_octiger \
]
verify_sprite_exists $rtn $id
br::list add $layers($lv.spr-list) $id
# Randomly position the sprite
random_position_sprite $id
return $id
}
#---------------------------------------------------------------------
# Routine: make_upfront
# Purpose: Support routine for "make_world"
# Arguments: objclass = Object-class name
# "objclass" may be any "upfront" class name. Examples include:
#
# ocintra ocscroll octree
# "make_world" calls this routine during the world-generation proce-
# dure to add zero or more instances of an "upfront" class.
#---------------------------------------------------------------------
dmproc 2 make_upfront { objclass } {
global gdata
set minnum [get_class_param $objclass minnum]
set maxnum [get_class_param $objclass maxnum]
set num [random_int $minnum $maxnum]
for { set ii 1 } { $ii <= $num } { incr ii } {
set gdata(force_create) 1
set cmd new_$objclass
eval $cmd
}
}
#---------------------------------------------------------------------
# Routine: make_world
# Purpose: Creates a new world
#
# Arguments: NewWorldName = Name of new world. For the first (or
# main) world, this should be equal to the following par-
# ameter: $gdata(WorldMain)
#
# FromPortalID = Sprite ID of the inter-world portal that
# led "forward" to the new world. If the main world is
# being created, this parameter should be equal to "none".
# "make_world" creates the specified world and calls "set_world_name"
# to assert that the active world name has changed.
# This routine creates "forward" portals, intra-world portals, and
# ocscrolls as requested by the world-definitions section associated
# with the specified world.
# "Reverse" portals are handled specially:
# If the new world is the main world, no "reverse" portals are cre-
# ated. "make_world" returns an empty string, in this case.
# If the new world is a deeper world, this routine assumes that world
# creation was triggered by the use of a "forward" portal located in
# the previous world, and that $FromPortalID specifies the sprite ID
# for the "forward" portal. In this case, this routine adds exactly
# one "reverse" portal to the new world and connects it to the "for-
# ward" portal specified by $FromPortalID. It then returns the sprite
# ID of the new "reverse" portal.
#---------------------------------------------------------------------
dmproc 100 make_world { NewWorldName FromPortalID } {
if { $DebugLevel >= 1 } {
puts "$rtn $NewWorldName $FromPortalID"
}
global gdata layers lv BRICKAPI FRAFMTRGB NRDIGITS
global fr2data fr3data MaxNumPortal WorldKeyStart
global RandomMapEnable
global RandomMapWidthMin RandomMapHeightMin
global RandomMapWidthMax RandomMapHeightMax
global BaseMapWidth BaseMapHeight
# Note: This code is biased towards the creation of horizontal worlds,
# but vertical worlds may be created as well.
for { set ii 1 } { $ii <= 3 } { incr ii } {
set RandomMapWidth [random_int \
$RandomMapWidthMin $RandomMapWidthMax ]
set RandomMapHeight [random_int \
$RandomMapHeightMin $RandomMapHeightMax ]
if { $RandomMapWidth >= $RandomMapHeight } { break }
}
if { $NewWorldName eq $gdata(WorldMain) } {
set FromWorldName none
} else {
set FromWorldName $lv
}
set_world_name $NewWorldName
set world_key $WorldKeyStart.$NewWorldName
set is_invariant 0
if { [info exists gdata($world_key.is_invariant)] && \
$gdata($world_key.is_invariant) } {
set is_invariant 1
}
if { $is_invariant } { # Special case - Predefined map is re-
# quired
if { [info exists gdata($world_key.map_data)] } {
set width $gdata($world_key.width)
set height $gdata($world_key.height)
set MapString $gdata($world_key.map_data)
if { ![string length $MapString] } {
set gdata($NewWorldName.is_empty) 1
}
} else {
puts "$IE-01: World $lv is flagged as invariant,\
but no map is provided"
puts "Either provide a map for the level or disable\
is is_invariant flag"
exit 1
}
} elseif { $RandomMapEnable } {
# Use a random map
set width $RandomMapWidth
set height $RandomMapHeight
set MapString [make_random_map $width $height]
} else {
# Use a predefined map
if { [info exists gdata($world_key.map_data) ] } {
set width $gdata($world_key.width)
set height $gdata($world_key.height)
set MapString $gdata($world_key.map_data)
} else {
puts "$IE-02: Random maps are disabled, but no\
default map is"
puts "provided for the following level: $lv"
puts "Either enable random maps or add a default map\
for the level in question"
exit 1
}
}
set layer_id [br::layer add]
set layers($lv) $layer_id
set layers($lv.width) $width
set layers($lv.height) $height
if { $BRICKAPI < 5300 } {
set info_list [br::layer info $layer_id]
set layers($lv.spr-list) [lindex $info_list 0]
set layers($lv.map) [lindex $info_list 1]
set layers($lv.str-list) [lindex $info_list 2]
} else {
set layers($lv.spr-list) [br::layer sprite-list $layer_id]
set layers($lv.map) [br::layer map $layer_id]
set layers($lv.str-list) [br::layer string-list $layer_id]
}
br::layer sorted $layer_id 1
# This code is an attempt to keep the "info" layer (i.e., the trans-
# parent layer that contains the game's information display) on top.
if [info exists layers(info)] {
set old_info $layers(info)
set old_lv $layers($lv)
br::layer swap $old_info $old_lv
set layers(info) $old_lv
set layers($lv) $old_info
}
set t2 [br::tile create]
set t3 [br::tile create]
set n1 [expr 8 * 8 * $NRDIGITS]
set n2 [string length $fr2data]
set n3 [string length $fr3data]
if { ($n1 != $n2) || ($n1 != $n3) } {
puts "$IE-03" ; exit 1
}
set fr2 [br::frame create $FRAFMTRGB 8 8 \
[binary format H$n2 $fr2data]]
set fr3 [br::frame create $FRAFMTRGB 8 8 \
[binary format H$n3 $fr3data]]
br::tile add-frame $t2 $fr2
br::tile add-frame $t3 $fr3
br::tile collides $t3 box
br::map tile-size $layers($lv.map) 8 8
br::map tile $layers($lv.map) 1 $t2
br::map tile $layers($lv.map) 2 $t3
br::map size $layers($lv.map) $width $height
set ExpectedSize [expr $width * $height * 4]
br::map set-data $layers($lv.map) \
[binary format H$ExpectedSize \
[string map {"-" 0100 "1" 0200} $MapString]]
# Create "upfront" objects as appro-
# priate
foreach objclass $gdata(list_classes_upfront) {
make_upfront $objclass
}
# Preload "periodic" objects as appro-
# priate
foreach objclass $gdata(list_classes_periodic) {
set prenum [get_class_param $objclass preload ]
set maxnum [get_class_param $objclass maxnum ]
if { $prenum > $maxnum } { set $prenum $maxnum }
for { set ii 1 } { $ii <= $prenum } { incr ii } {
set gdata(force_create) 1
set cmd new_$objclass
eval $cmd
}
}
# Create inter-world "forward" portals
set to_worlds $gdata($world_key.to_worlds)
foreach world $to_worlds {
new_ocinter "forward" $world none
}
; # Creating the main level?
if { $lv eq $gdata(WorldMain) } {
; # Yes - Presently, there's no reverse
; # portal in the main level
set PortalUpID ""
} else { ; # No - Add a reverse portal that re-
; # turns to the "forward" portal which
; # led here
set PortalUpID \
[new_ocinter "reverse" $FromWorldName $FromPortalID]
}
return $PortalUpID
}
#---------------------------------------------------------------------
# Routine: is_ocplayer_driving
# Purpose: Determines whether or not ocplayer is driving
# Arguments: None
# This routine returns nonzero if the ocplayer is driving and zero
# otherwise.
#---------------------------------------------------------------------
dmproc 5 is_ocplayer_driving {} {
global gdata
set frame_index $gdata(ocplayer.frame_index)
set driving_left $gdata(ocplayer.frame_index.driving_left)
set driving_right $gdata(ocplayer.frame_index.driving_right)
if { ($frame_index == $driving_left) || \
($frame_index == $driving_right) } { return 1 }
return 0
}
#---------------------------------------------------------------------
# Routine: cycle_smart
# Purpose: Motion-related support routine
# Arguments: id = Sprite ID
# This is a support routine that may be used by "run_" routines that
# switch occasionally between intelligent and unintelligent motion.
# For usage examples, see "run_ockarkinos" and "group_avoid_run".
#---------------------------------------------------------------------
dmproc 5 cycle_smart { id } {
global sdata
if { [info exists sdata($id.smart)] } {
if { $sdata($id.smart) < 0 } {
incr sdata($id.smart) 1
if { $sdata($id.smart) == 0 } {
incr sdata($id.smart) 1
}
}
}
}
#---------------------------------------------------------------------
# Routine: group_avoid_run
# Purpose: "run_..." support routine
# Arguments: id = Sprite ID
# objclass = Object-class name
# This routine may be used to handle "run_..." actions for classes
# similar to "ocmedical" or "ocmoney" at "run_..." time. Changes may
# be needed as new classes are added.
# Common characteristics of the supported classes: Avoidance of play-
# er. Collision with player destroys object and produces a bonus for
# player.
#---------------------------------------------------------------------
dmproc 100 group_avoid_run { id objclass } {
if { $DebugLevel >= 2 } { puts "$rtn $id $objclass" }
global gdata layers lv sdata
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_$objclass" } { return }
if { $objclass ne [get_sprite_class $id] } { return }
# Identify sprite class
set iddot "$sdata($id.)"
regsub -all {^run_} $iddot "" sprite_class
set is_ocmedical \
[if { $sprite_class eq "ocmedical" } {expr 1} {expr 0}]
set is_ocmoney \
[if { $sprite_class eq "ocmoney" } {expr 1} {expr 0}]
incr sdata($id.ct) ; # Increment timeline counter
set vx 0 ; # Reset velocity components
set vy 0
# Move the sprite periodically
if { !($sdata($id.ct) % $sdata($id.speed_divisor)) } {
set ocplayer_id $gdata($lv,ocplayer_id)
get_target_dx_dy $id $ocplayer_id dx dy
if {$dx < 0} { set dx [expr -$dx] }
if {$dy < 0} { set dy [expr -$dy] }
set cautious [get_object_param $id cautious]
if { ($dx > $cautious) || ($dy > $cautious) } {
# Object isn't cautious at this dis-
# tance
get_dir_vx_vy $sdata($id.dir) vx vy
} elseif { $sdata($id.smart) > 0 } {
# Moving intelligently - Avoid ocplayer
set ocplayer_id $gdata($lv,ocplayer_id)
get_target_dx_dy $id $ocplayer_id dx dy
if {$dx < 0} { set vx 1 }
if {$dx > 0} { set vx -1 }
if {$dy < 0} { set vy 1 }
if {$dy > 0} { set vy -1 }
} else {
# No: Random-motion mode
get_dir_vx_vy $sdata($id.dir) vx vy
cycle_smart $id ; # Return to intelligent mode eventual-
# ly
}
# Set sprite velocity
br::sprite vel $id $vx $vy
# Check to see if we've hit a wall
# Note: This sprite doesn't change
# direction randomly except when a
# collision occurs
if { [lindex [br::collision map $id \
$layers($lv.map) 1] 0] == 1 } {
# Hit a wall: Pick a new direction at
# random
set sdata($id.dir) [random_direction]
# Override intelligent behavior temp-
# orarily
if { $sdata($id.smart) > 0 } { set sdata($id.smart) -10 }
} else {
move_sprite $id ; # Move the sprite
}
}
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
# Collided with ocplayer?
if { $other_class eq "ocplayer" } {
# Yes
if { $is_ocmedical } {
set name [get_object_name_current $id]
if { ($name ne "none") } {
# Update the status line
set gdata(infomsg) "You ate $name"
}
# Adjust health level
set ocmedical_health \
[get_class_param ocmedical health]
set n [expr $gdata(health) + $ocmedical_health]
# Limit health to 100
if { $n > 100 } { set n 100 }
set gdata(health) $n
}
if { $is_ocmoney } {
# Value of ocmoney
set value $sdata($id.value)
# Update the status line
set gdata(infomsg) "Picked up $value coins"
# Adjust ocplayer's inventory
inventory_ocmoney_add $value
}
# Play the appropriate sound
play_sound bonus 0
# Destroy the affected sprite
destroy_sprite $sprite_class $id
}
}
}
#---------------------------------------------------------------------
# Routine: group_barnyard_run
# Purpose: Runs an object: classes in "barnyard" category
# Arguments: id = Sprite ID
# objclass = Object-class name
#---------------------------------------------------------------------
dmproc 100 group_barnyard_run { id objclass } {
if { $DebugLevel >= 2 } { puts "$rtn $id $objclass" }
global BRICKAPI
global gdata layers lv sdata level_list LevelToSData
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_$objclass" } { return }
if { $objclass ne [get_sprite_class $id] } { return }
; # Player ID
set ocplayer_id $gdata($lv,ocplayer_id)
set ocplayer_collide 0 ; # Flag: Player is at same position
incr sdata($id.ct) ; # Increment timeline counter
set vx 0 ; # Reset velocity components
set vy 0
# Move the sprite periodically
if { !($sdata($id.ct) % $sdata($id.speed_divisor)) } {
get_dir_vx_vy $sdata($id.dir) vx vy
br::sprite vel $id $vx $vy
# Check to see if we've hit a wall or
# if we're changing direction at ran-
# dom
set rthreshold 0.99
if { [lindex [br::collision map $id \
$layers($lv.map) 1] 0] == 1 || rand() > $rthreshold } {
# Yes: Pick a new direction at random
set sdata($id.dir) [random_direction]
} else {
move_sprite $id ; # Move the sprite
}
}
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
if { $other_class eq "ocplayer" } {
# Assert collision with ocplayer
set ocplayer_collide 1
# Set a collision lock to limit re-
# peated processing of the same colli-
# sion
if { ![info exists \
gdata($lv,$ocplayer_id.${objclass}_id) ] } {
set gdata($lv,$ocplayer_id.${objclass}_id) $id
# This code is executed once per col-
# lision
# Name of sound to play (may change
# before it's actually played)
set sound $objclass
# Check collision type
if { $objclass eq "occar" } {
# occar-ocplayer collision
# Change ocplayer sprite into occar
# sprite (as player is now driving)
set gdata(ocplayer.frame_index) \
$gdata(ocplayer.frame_index.driving_left)
br::sprite frame $ocplayer_id \
$gdata(ocplayer.frame_index.driving_left)
# Update the status line
set name [get_object_name_current $id]
if { $name eq "none" } { set name "the car" }
set gdata(infomsg) "You drive $name"
# Destroy the occar sprite
destroy_sprite $objclass $id
# Reset collision flag as the sprite
# is now gone
set ocplayer_collide 0
# Prevent the creation of more cars in
# this world
set gdata(WORLD_PARAM.$lv.occar_minnum) 0
set gdata(WORLD_PARAM.$lv.occar_maxnum) 0
} elseif { $objclass eq "occross" } {
# occross -ocplayer collision
# Destroy the affected sprite
destroy_sprite $objclass $id
# Reset collision flag as the sprite
# is now gone
set ocplayer_collide 0
# Embracing God is healthy
set gdata(health) 100
# Switch to "God" mode
if { $BRICKAPI >= 5400 } {
set godtime [expr int ([br::clock ms] / 1000)]
set gdata(godtime) $godtime
set gdata(ocplayer.frame_index) \
$gdata(ocplayer.frame_index.godmode)
br::sprite frame $ocplayer_id \
$gdata(ocplayer.frame_index.godmode)
}
}
# Play the appropriate sound
play_sound $sound 0
}
} elseif { $other_class eq "ockarkinos" } {
# Object eats an ockarkinos
# Update the status line
set name1 [get_object_name_current $id ]
set name2 [get_object_name_current $tgt_id ]
if { ($name1 ne "none") && ($name2 ne "none") } {
set gdata(infomsg) "$name1 ate $name2"
}
# Play the appropriate sound
play_sound pop 0
# Destroy the affected sprite
destroy_sprite $other_class $tgt_id
}
}
# If an ocplayer collided with an object supported by this routine
# previously, a collision lock was set to limit repeated processing
# of the (same) collision. The following code releases the lock after
# the ocplayer leaves the object's position.
set xlock_id $ocplayer_id.${objclass}_id
if { !$ocplayer_collide } {
if { [info exists gdata($lv,$xlock_id) ] } {
set glock_id $gdata($lv,$xlock_id)
if { $id eq $glock_id } {
unset gdata($lv,$xlock_id)
}
}
}
}
#---------------------------------------------------------------------
# Routine: group_hunter_run
# Purpose: Runs an object (multiple hunter classes)
# Arguments: id = Sprite ID
# objclass = Object-class name
# This routine handles object actions for hunter-group objects such as
# the following:
#
# ocdog ockarkinos octiger
#
# The common characteristic is that these objects seek the ocplayer
# (either to attack him/her or simply to follow).
#---------------------------------------------------------------------
dmproc 100 group_hunter_run { id objclass } {
if { $DebugLevel >= 2 } { puts "$rtn $id $objclass" }
global gdata layers lv sdata
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_$objclass" } { return }
if { $objclass ne [get_sprite_class $id] } { return }
# Set object-class flags
set is_ocdog 0 ; set is_ockarkinos 0 ; set is_octiger 0
if { $objclass eq "ocdog" } { set is_ocdog 1 }
if { $objclass eq "ockarkinos" } { set is_ockarkinos 1 }
if { $objclass eq "octiger" } { set is_octiger 1 }
if { !$is_ocdog && !$is_ockarkinos && !$is_octiger } { return }
incr sdata($id.ct) ; # Increment timeline counter
set is_hunter 0 ; # Reset "hunter" flag
set vx 0 ; # Reset velocity components
set vy 0
set ocplayer_collide 0 ; # Flag: Collided with ocplayer
; # ocplayer sprite id
set ocplayer_id $gdata($lv,ocplayer_id)
; # Check to see if ocplayer is hiding
; # behind a tree
if { [info exists gdata($lv,$ocplayer_id.octreehide_id) ] } {
set ocplayer_hidden 1
} else {
set ocplayer_hidden 0
}
# Move the sprite periodically
if { !($sdata($id.ct) % $sdata($id.speed_divisor)) } {
# Is this a hunter?
if { ($sdata($id.smart) > 0) && !$ocplayer_hidden } {
# Yes - Hunt the ocplayer!
set is_hunter 1
get_target_dx_dy $id $ocplayer_id dx dy
if {$dx < 0} { set vx -1 }
if {$dx > 0} { set vx 1 }
if {$dy < 0} { set vy -1 }
if {$dy > 0} { set vy 1 }
# An "ocdog" seeks the "ocplayer" until the two are reasonably close.
# Then the "ocdog" reverts to random motion until the distance in-
# creases again. The net effect is that the "ocdog" tends to follow
# the "ocplayer".
if { $is_ocdog } {
if {$dx < 0} { set dx [expr -$dx] }
if {$dy < 0} { set dy [expr -$dy] }
if { ($dx < 25) && ($dy < 25) } {
set sdata($id.smart) -8
}
}
} else {
# No: It's a grazer
get_dir_vx_vy $sdata($id.dir) vx vy
cycle_smart $id ; # Or maybe a hunter in explorer mode
}
br::sprite vel $id $vx $vy
set rthreshold 0.99
if { $is_hunter } { set rthreshold 1.00 }
# Check to see if we've hit a wall or
# if we're changing direction at ran-
# dom
if { [lindex [br::collision map $id \
$layers($lv.map) 1] 0] == 1 || rand() > $rthreshold } {
# Yes: Pick a new direction at random
set sdata($id.dir) [random_direction]
# Override intelligent behavior temp-
# orarily
if { $sdata($id.smart) > 0 } { set sdata($id.smart) -8 }
} else {
move_sprite $id ; # Move the sprite
}
}
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
set a_eats_b 0 ; # Flag: Active object eats struck one
set b_eats_a 0 ; # Flag: Struck object eats active one
# octigers eat occows
if { $is_octiger && ($other_class eq "occow") } {
set a_eats_b 1
}
# octiger and ocdog are evenly matched
if { ($is_octiger && ($other_class eq "ocdog" )) || \
($is_ocdog && ($other_class eq "octiger")) } {
set n [random_int 1 2]
if { $n == 1 } { set a_eats_b 1 }
if { $n == 2 } { set b_eats_a 1 }
}
if { $other_class eq "ocplayer" } {
# Collided with an ocplayer
# Effect on ocplayer's health
set heffect [get_object_param $id heffect]
# Assert collision with ocplayer
set ocplayer_collide 1
# Set a collision lock to limit object
# class sounds to once per collision
if { ![info exists \
gdata($lv,$ocplayer_id.${objclass}_id) ] } {
set gdata($lv,$ocplayer_id.${objclass}_id) $id
# Play object-class sound once per
# collision
play_sound $objclass 0
} elseif { $heffect != 0 } {
# "Hit" sound may be played repeatedly
# while a collision continues
play_sound hit 5
}
# Determine effect on ocplayer health
set heffect [get_object_param $id heffect]
if { [is_ocplayer_driving] } { set heffect 0 }
# Update the status line
set name [get_object_name_current $id]
if { $is_ockarkinos } {
set gdata(infomsg) "$name attacks"
} elseif { $is_octiger } {
set gdata(infomsg) "$name is hungry"
}
# Adjust ocplayer health
incr gdata(health) $heffect
# "God" mode prevents damage
if { [info exists gdata(godtime)] } {
set gdata(health) 100
}
} elseif { $a_eats_b } {
# Active object eats other one
# Update the status line
set name1 [get_object_name_current $id ]
set name2 [get_object_name_current $tgt_id ]
if { ($name1 ne "none") && ($name2 ne "none") } {
set gdata(infomsg) "$name1 ate $name2"
}
# Play the appropriate sound
play_sound $other_class 0
# Destroy the affected sprite
destroy_sprite $other_class $tgt_id
} elseif { $b_eats_a } {
# Other object eats active one
# Update the status line
set name1 [get_object_name_current $id ]
set name2 [get_object_name_current $tgt_id ]
if { ($name1 ne "none") && ($name2 ne "none") } {
set gdata(infomsg) "$name2 ate $name1"
}
# Play the appropriate sound
play_sound $objclass 0
# Destroy the affected sprite
destroy_sprite $objclass $id
}
}
# If an ocplayer collided with an object supported by this routine
# previously, a collision lock was set to limit repeated processing
# of the (same) collision. The following code releases the lock after
# the ocplayer leaves the object's position.
set xlock_id $ocplayer_id.${objclass}_id
if { !$ocplayer_collide } {
if { [info exists gdata($lv,$xlock_id) ] } {
set glock_id $gdata($lv,$xlock_id)
if { $id eq $glock_id } {
unset gdata($lv,$xlock_id)
}
}
}
}
#---------------------------------------------------------------------
# Routine: run_ocbullet
# Purpose: Runs an object: ocbullet class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocbullet { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocbullet" } { return }
if { "ocbullet" ne [get_sprite_class $id] } { return }
move_sprite $id ; # Move the sprite
# Check for collisions with shootable
# sprites
foreach tgt [collision_sprites $id] {
set tgt_id [lindex $tgt 1]
set tgt_type "$sdata($tgt_id.)"
regsub -all {^run_} $tgt_type "" objclass
# Was a shootable sprite hit?
if { [get_object_param $tgt_id shoot_can] } {
# Yes
# Display a status message
set name [get_object_name_current $tgt_id]
if { $name ne "none" } {
# Update the status line
set gdata(infomsg) "You shot $name"
}
# Get object parameters
foreach param [list \
health shoot_score shoot_effect \
sound_destroy sound_hit] {
set $param [get_object_param $tgt_id $param]
}
# Update object health
set new_health [expr $health + $shoot_effect]
set_object_param $tgt_id health $new_health
# Some sound-related conventions: If an object is hit or destroyed
# here, an associated sound may be played. The "destroy" sound de-
# faults to the object's main class sound (if any). The "hit" sound
# has no default.
# Has object been destroyed?
if { $new_health < 0 } {
# Yes - Play appropriate sounds
if { $sound_destroy eq "0" } {
set sound_destroy $objclass
}
play_sound $sound_destroy 100
play_sound pop 0
# Update score appropriately
incr gdata(score) $shoot_score
# Destroy the affected sprite
destroy_sprite $objclass $tgt_id
} elseif { $sound_hit ne "0" } {
# Play appropriate sound if it exists
play_sound $sound_hit 100
}
# Flag this ocbullet for removal
set remove_ocbullet YES
}
}
# Remove this ocbullet?
if { [info exists remove_ocbullet] || \
[lindex [br::collision map $id $layers($lv.map) 1] 0] } {
# Yes
destroy_sprite ocbullet $id
}
}
#---------------------------------------------------------------------
# Routine: run_occar
# Purpose: Runs an object: occar class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_occar { id } {
group_barnyard_run $id occar
}
#---------------------------------------------------------------------
# Routine: run_occow
# Purpose: Runs an object: occow class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_occow { id } {
group_barnyard_run $id occow
}
#---------------------------------------------------------------------
# Routine: run_occross
# Purpose: Runs an object: occross class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_occross { id } {
group_barnyard_run $id occross
}
#---------------------------------------------------------------------
# Routine: run_ocdog
# Purpose: Runs an object: ocdog class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocdog { id } {
group_hunter_run $id ocdog
}
#---------------------------------------------------------------------
# Routine: run_ocflames
# Purpose: Runs an object: ocflames class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocflames { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata level_list LevelToSData
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocflames" } { return }
if { "ocflames" ne [get_sprite_class $id] } { return }
if { ![info exists sdata($id.frametick)] } {
set sdata($id.frametick) 0
}
set FPSDIV 10 ; # This should be changed into a class
; # parameter
if { [incr sdata($id.frametick)] == $FPSDIV } {
set sdata($id.frametick) 0
set n [expr $gdata(ocflames.frame_index) + 1]
if { $n >= $gdata(ocflames.num_frames) } { set n 0 }
set gdata(ocflames.frame_index) $n
br::sprite frame $id $n
}
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
if { $other_class eq "ocplayer" } {
# Player hit! Adjust health
incr gdata(health) [get_object_param $id heffect]
# "God" mode prevents damage
if { [info exists gdata(godtime)] } {
set gdata(health) 100
}
# Play appropriate sound
if { $sdata($id.frametick) == 0 } {
play_sound ocflames 0
}
}
}
}
#---------------------------------------------------------------------
# Routine: run_ocinter
# Purpose: Runs an object: ocinter class
# Arguments: id = Sprite ID
# This routine runs two different (but related) types of objects:
# inter-world forward and reverse portals.
#---------------------------------------------------------------------
dmproc 100 run_ocinter { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata level_list
global LevelToSData WorldKeyStart
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocinter" } { return }
if { "ocinter" ne [get_sprite_class $id] } { return }
set FromWorldName $lv
set FromPortalID $id
set ocplayer_id $gdata($lv,ocplayer_id)
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
# Player-portal collision?
if { $other_class eq "ocplayer" } {
# Yes
# Consistency check
if { $ocplayer_id ne $tgt_id } {
puts "$IE-01: $rtn: Inconsistent ocplayer IDs"
exit 1
}
# Did player get here by using the
# portal (or its other side) ?
if { [info exists gdata($lv.$tgt_id.$id.portal_lock)] } {
# Yes - So the portal shouldn't acti-
# vate again immediately
return
} else {
# No - Lock the portal to prevent in-
# finite loops
set gdata($lv.$tgt_id.$id.portal_lock) 1
}
if { $DebugLevel } {
puts "$rtn: ocplayer $lv,$tgt_id collided with\
portal $lv,$id"
}
# Update the status line
set gdata(infomsg) "Used a world portal"
# Play the appropriate sound
play_sound ocinter 0
set to_world $sdata($id.to_world)
set to_portal $sdata($id.to_portal)
if {[info exists LevelToSData($to_world)]} {
if { $DebugLevel > 2 } {
puts "$rtn: to_world $to_world exists"
}
array set xdata [array get sdata]
unset sdata
array set sdata $LevelToSData($to_world)
set_world_name $to_world
if { $to_portal eq "none" } {
set to_portal [new_ocinter \
"reverse" $FromWorldName $FromPortalID]
verify_sprite_exists "$rtn-1000" $to_portal
set xdata($FromPortalID.to_portal) $to_portal
}
set LevelToSData($FromWorldName) [array get xdata]
set PortalIdExit $to_portal
verify_sprite_exists "$rtn-1001" $PortalIdExit
} else {
# Consistency check
if { $to_world eq $gdata(WorldMain) } {
puts "$IE-02" ; exit 1
}
array set xdata [array get sdata]
unset sdata
# Note: "make_world" sets $lv equal to
# $to_world
set PortalIdExit [make_world $to_world $id]
verify_sprite_exists $rtn $PortalIdExit
new_ocplayer
set xdata($FromPortalID.to_portal) $PortalIdExit
set LevelToSData($FromWorldName) [array get xdata]
set LevelToSData($to_world) [array get sdata]
}
# Consistency check
if { $lv ne $to_world } { puts "$IE-03" ; exit 1 }
set MyPosition [br::sprite pos $PortalIdExit]
set my_x [lindex $MyPosition 0]
set my_y [lindex $MyPosition 1]
set ocplayer_id $gdata($lv,ocplayer_id)
set frame_index $gdata(ocplayer.frame_index)
set frame_godmode $gdata(ocplayer.frame_index.godmode)
set frame_normal $gdata(ocplayer.frame_index.normal)
if { ($frame_index != $frame_godmode) &&
($frame_index != $frame_normal) } {
set gdata(ocplayer.frame_index) $frame_normal
br::sprite frame $ocplayer_id $frame_normal
}
set gdata($lv.$ocplayer_id.$PortalIdExit.portal_lock) 1
if { ![info exists gdata(ocplayer.frame_index)] } {
puts "$IE-03"
exit 1
}
br::sprite frame $ocplayer_id $gdata(ocplayer.frame_index)
br::sprite pos $ocplayer_id $my_x $my_y
br::sprite vel $ocplayer_id 0 0
# The following block is an attempt to prevent a problem. Under some
# conditions, the player may overlap a wall as soon as he/she emerges
# from a portal. If this happens, and isn't corrected here, collision-
# detection code in "run_ocplayer" may be confused and prevent the
# player from moving. To address the issue, if the player intersects a
# wall after arrival through a portal, this code bounces him/her to a
# random (but collision-free) location.
# Colliding on arrival?
set colls [br::collision map $ocplayer_id \
$layers($lv.map) 1]
if { [lindex $colls 0] } {
# Yes - Bounce to a better location
random_position_sprite $ocplayer_id
}
# Account for player's position
account_ocplayer_position
foreach name [array names LevelToSData] {
if { $name ne $to_world } {
br::layer visible $layers($name) 0
}
}
# Make world layer visible
br::layer visible $layers($to_world) 1
# Made it to the exit?
if { $to_world eq $gdata(WorldEndOfAllSongs) } {
# Yes - Play appropriate sound
play_sound win 2600
# Disable normal "exit" sound
unset gdata(sound_exit)
# Update the status line
set gdata(infomsg) "Winner!"
# Display a farewell message
show_msg " Made it to the exit " 70 80
quit_program ; # Quit the program
}
return
}
}
# If we make it to this point, the ocplayer sprite ($ocplayer_id) in
# the current world ($lv) isn't standing on the current portal ($id),
# so the portal should be unlocked. The following code addresses the
# issue.
if { [info exists gdata($lv.$ocplayer_id.$id.portal_lock)] } {
unset gdata($lv.$ocplayer_id.$id.portal_lock)
}
}
#---------------------------------------------------------------------
# Routine: run_ocintra
# Purpose: Runs an object: ocintra class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocintra { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocintra" } { return }
if { "ocintra" ne [get_sprite_class $id] } { return }
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
# Collided with ocplayer?
if { $other_class eq "ocplayer" } {
# Yes
# Update the status line
set gdata(infomsg) "Used a local portal"
# Play the appropriate sound
play_sound ocintra 0
# Set new position
random_position_sprite $tgt_id
# Account for new ocplayer position
account_ocplayer_position
}
}
}
#---------------------------------------------------------------------
# Routine: run_ockarkinos
# Purpose: Runs an object: ockarkinos class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ockarkinos { id } {
group_hunter_run $id ockarkinos
}
#---------------------------------------------------------------------
# Routine: run_ocmedical
# Purpose: Runs an object: ocmedical class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocmedical { id } {
group_avoid_run $id ocmedical
}
#---------------------------------------------------------------------
# Routine: run_ocmoney
# Purpose: Runs an object: ocmoney class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocmoney { id } {
group_avoid_run $id ocmoney
}
#---------------------------------------------------------------------
# Routine: run_ocpig
# Purpose: Runs an object: ocpig class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocpig { id } {
group_barnyard_run $id ocpig
}
#---------------------------------------------------------------------
# Routine: run_ocplayer
# Purpose: Runs an object: ocplayer class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocplayer { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv proto sdata
global KeyH_Button KeyH_Input
global KeyI_Button KeyI_Input
global KeyQ_Button KeyQ_Input
global KeySpace_Button KeySpace_Input
global BRICKAPI MaxGodTime
set ocbullet_maxnum [get_class_param ocbullet maxnum]
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocplayer" } { return }
if { "ocplayer" ne [get_sprite_class $id] } { return }
# Fetch input
set io(1) [br::io fetch 1]
set io(0) [br::io fetch 0]
set hkey [lindex $io($KeyH_Input) 2 $KeyH_Button]
set ikey [lindex $io($KeyI_Input) 2 $KeyI_Button]
set qkey [lindex $io($KeyQ_Input) 2 $KeyQ_Button]
set spkey [lindex $io($KeySpace_Input) 2 $KeySpace_Button ]
set horiz [lindex $io(0) 0 0]
set vert [lindex $io(0) 0 1]
if { [lindex $io(0) 7] || $qkey || [br::io has-quit] } {
quit_program
}
if { $hkey } { display_help ; return }
if { $ikey } { display_inventory ; return }
# Get ocplayer movement
set vx [expr { $horiz < 0 ? -1 : ($horiz > 0 ? 1 : 0) }]
set vy [expr { $vert < 0 ? -1 : ($vert > 0 ? 1 : 0) }]
br::sprite vel $id $vx $vy
# Note: If the ocplayer is driving, he/she can pass through walls.
# This is a kludge; it's necessary (or may be necessary) to keep the
# ocplayer (in driving mode) from getting stuck behind passages that
# are too narrow for an occar to use.
# Check for collision with walls
if { ![is_ocplayer_driving] } {
set colls [br::collision map $id $layers($lv.map) 1]
set vx [expr {[lindex $colls 1] + [lindex $colls 3]}]
set vy [expr {[lindex $colls 2] + [lindex $colls 4]}]
}
# Set new position
incr sdata($id.px) $vx
incr sdata($id.py) $vy
br::sprite pos $id $sdata($id.px) $sdata($id.py)
handle_limbo $id
account_ocplayer_position ; # Needed because "handle_limbo" may
# have moved the ocplayer
# If there's any movement, save the
# direction for use in subsequent
# shooting operations
if { $horiz || $vert } {
set sdata($id.gx) $vx
set sdata($id.gy) $vy
}
# "God mode" code
if { $BRICKAPI >= 5400 } {
if { [info exists gdata(godtime)] } {
set gdata(health) 100
# Has time in "God mode" ended?
set godtime $gdata(godtime)
set curtime [expr int ([br::clock ms] / 1000)]
if { ($curtime - $godtime) > $MaxGodTime } {
# Yes
unset gdata(godtime)
set gdata(ocplayer.frame_index) \
$gdata(ocplayer.frame_index.default)
br::sprite frame $id \
$gdata(ocplayer.frame_index.default)
}
}
}
set frame_index $gdata(ocplayer.frame_index)
set driving_left $gdata(ocplayer.frame_index.driving_left)
set driving_right $gdata(ocplayer.frame_index.driving_right)
if { ($frame_index == $driving_left) && ($vx > 0) } {
set gdata(ocplayer.frame_index) $driving_right
br::sprite frame $id $driving_right
} elseif { ($frame_index == $driving_right) && ($vx < 0) } {
set gdata(ocplayer.frame_index) $driving_left
br::sprite frame $id $driving_left
}
# Handle a shot (if any)
if { [lindex $io(0) 2 0] || $spkey } {
# If the ocplayer is driving, just
# beep the horn
if { [is_ocplayer_driving] } {
play_sound occar 250
} elseif { ([get_class_counter ocbullet] \
< $ocbullet_maxnum) && \
!$sdata($id.shot) && \
($sdata($id.gx) || $sdata($id.gy)) } {
# Create an ocbullet
incr_class_counter ocbullet
set ocbullet [br::sprite copy $proto(ocbullet)]
if { $DebugLevel } { puts "new ocbullet: $lv,$id" }
br::sprite pos $ocbullet [expr {$sdata($id.px) + 1}] \
[expr {$sdata($id.py) + 1}]
br::sprite vel $ocbullet [expr {$sdata($id.gx) * 2}] \
[expr {$sdata($id.gy) * 2}]
# Add it to the lists
br::list add $layers($lv.spr-list) $ocbullet
set sdata($ocbullet.) run_ocbullet
set sdata($id.shot) 1
# Play the appropriate sound
play_sound gunshot 0
# Shooting while hiding behind an
# octree breaks cover
if { [info exists gdata($lv,$id.octreehide_id) ] } {
unset gdata($lv,$id.octreehide_id)
}
}
} else {
set sdata($id.shot) 0 ; # Reset trigger for next shot
}
# Track ocplayer with camera
track_sprite $sdata($id.px) $sdata($id.py)
}
#---------------------------------------------------------------------
# Routine: run_ocscroll
# Purpose: Runs an object: ocscroll class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_ocscroll { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata level_list LevelToSData
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_ocscroll" } { return }
if { "ocscroll" ne [get_sprite_class $id] } { return }
# Player sprite ID
set ocplayer_id $gdata($lv,ocplayer_id)
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
# Player-portal collision?
if { $other_class eq "ocplayer" } {
# Yes
# Consistency check
if { $ocplayer_id ne $tgt_id } {
puts "$IE-01: $rtn: Inconsistent ocplayer IDs"
exit 1
}
if { $DebugLevel } {
puts "$rtn: ocplayer $lv,$tgt_id collided with\
ocscroll $lv,$id"
}
# Destroy the affected sprite
destroy_sprite ocscroll $id
# Update the status line
set gdata(infomsg) "You read a scroll"
display_wisdom ; # Display contents of ocscroll
}
}
}
#---------------------------------------------------------------------
# Routine: run_octiger
# Purpose: Runs an object: octiger class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_octiger { id } {
group_hunter_run $id octiger
}
#---------------------------------------------------------------------
# Routine: run_octree
# Purpose: Runs an object: octree class
# Arguments: id = Sprite ID
#---------------------------------------------------------------------
dmproc 100 run_octree { id } {
if { $DebugLevel >= 2 } { puts "$rtn $id" }
global gdata layers lv sdata level_list LevelToSData
# Safety measure; see comments in main
# routine
if { ![info exists sdata($id.)] } { return }
set callback $sdata($id.)
if { $callback ne "run_octree" } { return }
if { "octree" ne [get_sprite_class $id] } { return }
# Player sprite ID
set ocplayer_id $gdata($lv,ocplayer_id)
set ocplayer_collide 0
# Instantiate a tiger if appropriate
if { [get_object_param $id add_octiger] } {
get_target_dx_dy $id $ocplayer_id dx dy
if {$dx < 0} { set dx [expr -$dx] }
if {$dy < 0} { set dy [expr -$dy] }
set octigerdelta [get_class_param octree octigerdelta]
if { ($dx < $octigerdelta) && ($dy < $octigerdelta) } {
set octiger_id [new_octiger]
if { $octiger_id ne "0" } {
set MyPosition [br::sprite pos $id]
set my_x [lindex $MyPosition 0]
set my_y [lindex $MyPosition 1]
br::sprite pos $octiger_id $my_x $my_y
set_object_param $id add_octiger 0
play_sound octiger 0
}
}
}
# Check for collisions
foreach tgt [collision_sprites $id] {
# Process next collided object
set tgt_id [lindex $tgt 1]
# Class of other object involved
set other_class [get_sprite_class $tgt_id]
# Player-octree collision?
if { $other_class eq "ocplayer" } {
# Yes
# Consistency check
if { $ocplayer_id ne $tgt_id } {
puts "$IE-01: Inconsistent ocplayer IDs"
exit 1
}
# Player can hide here, subject to
# limitations (this only works once
# per octree, and shooting breaks con-
# cealment)
if { ![info exists \
gdata($lv,$id,$ocplayer_id.octreehide_flag) ] } {
set gdata($lv,$id,$ocplayer_id.octreehide_flag) 1
set gdata($lv,$ocplayer_id.octreehide_id) $id
}
# Assert collision with ocplayer
set ocplayer_collide 1
}
}
# If an ocplayer collided with an octree previously, a collision lock
# was set to limit repeated processing of the (same) collision. The
# following code releases the lock after the ocplayer leaves the
# octree's position.
if { !$ocplayer_collide } {
if { [info exists gdata($lv,$ocplayer_id.octreehide_id) ] } {
set octreehide_id $gdata($lv,$ocplayer_id.octreehide_id)
if { $id eq $octreehide_id } {
unset gdata($lv,$ocplayer_id.octreehide_id)
}
}
}
}
#---------------------------------------------------------------------
# Routine: setup_info_display
# Purpose: Sets up program's information display
# Arguments: None
#---------------------------------------------------------------------
dmproc 1 setup_info_display {} {
global gdata layers
global BGHeight BGWidth
global BGTileWidth BGTileHeight
global BRICKAPI FRAFMTTRA NCDIGITS TRANSPARFRA
set foo ""
set nn [expr $BGTileWidth * $BGTileHeight]
for { set ii 1 } { $ii <= $nn } { incr ii } \
{ append foo $TRANSPARFRA }
# Total number of hex digits
set NumHexDigits [expr $nn * $NCDIGITS]
# Consistency check
if { $NumHexDigits != [string length $foo] } {
puts "$IE-01" ; exit 1
}
set t1 [br::tile create]
if { $BRICKAPI < 5400 } {
global CHROMA_R CHROMA_G CHROMA_B
set fr1 [br::frame create $FRAFMTTRA \
$BGTileWidth $BGTileHeight \
[binary format H$NumHexDigits $foo] \
$CHROMA_R $CHROMA_G $CHROMA_B]
} else {
set fr1 [br::frame create $FRAFMTTRA \
$BGTileWidth $BGTileHeight \
[binary format H$NumHexDigits $foo]]
}
br::tile add-frame $t1 $fr1
set layer_id [br::layer add]
set layers(info) $layer_id
if { $BRICKAPI < 5300 } {
set info_list [br::layer info $layer_id]
set layers(info.spr-list) [lindex $info_list 0]
set layers(info.map) [lindex $info_list 1]
set layers(info.str-list) [lindex $info_list 2]
} else {
set layers(info.spr-list) [br::layer sprite-list $layer_id]
set layers(info.map) [br::layer map $layer_id]
set layers(info.str-list) [br::layer string-list $layer_id]
}
br::map tile-size $layers(info.map) $BGTileWidth $BGTileHeight
br::map tile $layers(info.map) 1 $t1
br::map size $layers(info.map) \
$BGWidth $BGHeight
br::map set-data $layers(info.map) \
[binary format H[expr {4 * $BGWidth * $BGHeight}] \
[string repeat 0100 [expr {$BGWidth * $BGHeight}]]]
set stg_x 10
set stg_y 10
set mxpos(time) 10 ; set mypos(time) 10
set mxpos(health) 120 ; set mypos(health) 10
set mxpos(score) 10 ; set mypos(score) 18
set mxpos(lv) 120 ; set mypos(lv) 18
set mxpos(infomsg) 10 ; set mypos(infomsg) 26
foreach stg { time health score lv infomsg } {
set gdata(stg.$stg) [br::string create]
set stg_x $mxpos($stg)
set stg_y $mypos($stg)
br::string position $gdata(stg.$stg) $stg_x $stg_y
br::list add $layers(info.str-list) $gdata(stg.$stg)
}
global col2fmt
set col2fmt "%-16s"
trace add variable gdata(time) write {apply {{a1 a2 op} { \
global idsfmt ; upvar 1 $a1 a ; \
br::string text $a(stg.time) [format $idsfmt \
"Time |[clock format $a(time) -format %M:%S]"] }}}
trace add variable gdata(health) write {apply {{a1 a2 op} { \
global col2fmt ; upvar 1 $a1 a ; \
br::string text $a(stg.health) [format $col2fmt \
"Health|$a(health)"] }}}
trace add variable gdata(score) write {apply {{a1 a2 op} { \
global idsfmt ; upvar 1 $a1 a ; \
br::string text $a(stg.score) [format $idsfmt \
"Score |$a(score)"] }}}
trace add variable gdata(lv) write {apply {{a1 a2 op} { \
global col2fmt ; upvar 1 $a1 a ; \
br::string text $a(stg.lv) [format $col2fmt \
"World |$a(lv)"] }}}
trace add variable gdata(infomsg) write {apply {{a1 a2 op} { \
global idsfmt ; upvar 1 $a1 a ; \
br::string text $a(stg.infomsg) [format $idsfmt \
"Info |$a(infomsg)"] }}}
}
#---------------------------------------------------------------------
# Routine: start_traces
# Purpose: Starts traces associated with info display
# Arguments: None
# This routine initializes some global variables associated with the
# program's information display. As a side effect, this starts the
# Tcl-level traces that run the display.
#---------------------------------------------------------------------
dmproc 1 start_traces {} {
global gdata lv
# Global data connected to traces
set gdata(health) 100
set gdata(score) 0
set gdata(start_time) [clock seconds]
set gdata(lv) $lv
set gdata(infomsg) "Press h for help"
}
#---------------------------------------------------------------------
# Routine: setup_program
# Purpose: Sets up the program
# Arguments: None
# This routine should be called once, immediately before the program's
# main loop is started. It handles all program-setup operations that
# aren't addressed by "non-proc" code.
#---------------------------------------------------------------------
dmproc 1 setup_program {} {
global gdata
setup_graphics ; # Set up graphics
setup_audio ; # Set up audio
setup_keyboard ; # Set up keyboard
setup_background ; # Set up background
setup_sprite_prototypes ; # Set up sprite prototypes
; # Make main world
make_world $gdata(WorldMain) none
new_ocplayer ; # Create an ocplayer
setup_info_display ; # Set up info display
display_msg_startup ; # Display a startup message
start_traces ; # Make info display active
}
#---------------------------------------------------------------------
# Routine: main_routine
# Purpose: Program's main routine
# Arguments: None
# This routine handles almost everything:
#
# (a) Most program-setup operations (*)
# (b) Main loop
# (c) Farewell message
# (*) Presently, some program-setup operations are handled by "non-
# proc" code.
#---------------------------------------------------------------------
dmproc 1 main_routine {} {
global BRICKAPI FPS lv gdata sdata
setup_program ; # Set up the program
while { $gdata(health) > 0 } {
# Run callbacks for sprites
foreach { id callback } [array get sdata *.] {
# The "info exists" test below is an attempt to prevent callbacks re-
# lated to sprites that are removed from play mid-loop. It appears
# that the test may not be completely reliable. A sprite that's remov-
# ed may apparently be replaced with a different sprite which has the
# same sprite ID. If the removal and replacement occur during the same
# iteration, erroneous callbacks may be made. To reduce the chances of
# problems, the callback routines have been modified so that they
# simply return if an inconsistency of this type is detected.
if { [info exists sdata($id)] } {
set oldlv $lv
$callback [string trim $id .]
# If we've changed worlds, we need to
# exit the inner loop immediately
if { $oldlv ne $lv } { break }
}
}
# Take care of necessary business
foreach objclass $gdata(list_classes_periodic) {
set cmd new_$objclass
eval $cmd
}
set gdata(time) [expr {[clock seconds] - $gdata(start_time)}]
br::render display
if { $BRICKAPI < 5400 } {
br::delay $FPS
} else {
br::clock wait $FPS
}
}
play_sound loser 2600 ; # Play an appropriate sound
play_sound loser 2600 ; # Twice
unset gdata(sound_exit) ; # Disable the normal "exit" sound
# Update the status line
set gdata(infomsg) "Loser!"
; # Display a farewell message
show_msg " You have perished " 80 80
quit_program ; # Quit the program
}
#---------------------------------------------------------------------
# Main program.
# This is the main program. It simply calls the main routine, which
# doesn't return.
main_routine