Let's Pack Heat!
In my previous post I described the process for adding new monsters to Ecliptic. This time, let's talk about some weapons that can be used to turn those monsters into green goo or a fine red mist.
Just your standard sci-fi armory - laser carbine, grenade launcher, pulse rifle, flamethrower and shotgun. I really like the look of the grenade launcher but the pulse rifle needs some more work.
Weapons in Ecliptic are Items - objects that the robots can interact with in some way. Pretty much everything in the game that isn't a Player or Monster is an Item. The game engine doesn't have any particular knowledge of Weapons, it just provides operations that can be used to implement them - target selection, animations, area effects and damage.
Most weapons are organized into a type hierarchy with a root type defining some common attributes and base types for melee and ranged weapons. Some items that you would classify as a weapon don't really fit into this hierarchy but that's OK - there's no rule that says only Official Weapons can be used to smash aliens into little bits. However, in this post, I'm mostly going to talk about ranged weapons.
Ranged weapons are weapons that throw some sort of projectile. They vary in power, range and the type of damage they do. A base type provides some common attributes such as charge - the number of shots left before a reload is required, max_charge - the maximum number of shots the weapon can hold, and range - the distance available for target selection.
The base type also defines some events that occur while the weapon is being used: needs_reload - emitted when you're out of ammo and fired - emitted when a target has been successfully selected.
Begin Item ID = ranged_weapon_base Base = $game.item.weapon_base Static = True Begin Attribute ID = charge BitCount = 4 Value = 0 End Begin Attribute ID = maxCharge BitCount = 4 Value = 0 End Begin Event ID = needs_reload End Begin Event ID = fired End
When the player attempts to use the weapon, the game will request they select a target from the map. You can select any square of the map visible to the robot - it doesn't matter if there's an enemy there or not.
; ; Invoked when the player uses a ranged weapon. ; Begin Handler EventID = $game.event.use MatchTarget = true Begin *select_target ; ; Out of ammo? ; Push $game.item.ranged_weapon_base.attribute.charge Push 0 Cmp Equal Jump need_reload Push[en] "Select a target" Push 0 Log ; ; Ask the player to choose ; a target location. ; *arg !Event:Object !selected *select_map_tile $game.item.ranged_weapon_base.event.target_selected $game.item.ranged_weapon_base.attribute.range 1 Exit ; ; No ammo so as for a reload. ; Label need_reload *emit_event $game.item.ranged_weapon_base.event.needs_reload 0 End
The *select_map_tile macro triggers an event that switches the game into selection mode. By default, the player is prompted to choose a location on the map but it has options for specifying selecting monsters, other robots or items from the map or inventory.
Once the player has selected a location, the reply event - $game.item.ranged_weapon_base.event.target_selected - will be triggered and the next event handler will run.
Begin Handler EventID = $game.item.ranged_weapon_base.event.target_selected MatchObject = True Begin ; ; Emit an event that causes the robot's ; action points to be reduced. ; *emit_object_used ; ; Elide some stuff that generates a ; "Target Selected" log message ; ; ; Reduce the ammo count. ; *select_object Push 1 Push $game.item.ranged_weapon_base.attribute.charge Sub Store $game.item.ranged_weapon_base.attribute.charge ; ; Trigger the derived type's fired handler ; *emit_event $game.item.ranged_weapon_base.event.fired 0 End End
THIS IS MY BOOMSTICK!
The type that derives from ranged_weapon_base needs to provide a handler for the fired event that will trigger the weapon's effect. This handler is from the Shotgun weapon.
Begin Handler EventID = $game.item.ranged_weapon_base.event.fired MatchObject = true Begin ; ; Trigger the "bullet_large_fire" effect ; starting from the robot's location and ; ending at the selected map location. ; *select_actor *arg !Effect:Position !Player:Position *select_event *arg !Effect:TargetPosition !Event:Position *arg $game.effect.bullet_large_fire.attribute.hit_strength 20 *start_effect $game.effect.bullet_large_fire 2 ; ; Play the shotgun sound. ; *start_sound $game.soundset.shotgun.sound.shotgun_fire 0 End End
Effective!
Similar to the way monster attacks work, weapons trigger effects when they are used. An effect is a re-usable object that can define its own events, handlers and attributes. In this case, the shotgun triggers the $game.effect.bullet_large_fire effect that plays a muzzle flash animation and then shows a bullet moving to the target. This effect is used by any weapon that throws a large bullet and can be customized with different hit_strength values.
Begin Effect ID = bullet_large_fire ; ; Attribute use to control how much ; damage the effect will do. ; Begin Attribute ID = hit_strength BitCount = 8 End ; ; Event triggered when the muzzle ; flash animation completes. ; Begin Event ID = bullet_large_launch End ; ; Event triggered when the bullet ; reaches the target. ; Begin Event ID = bullet_large_done End ; ; Even triggered when the "hit" ; animation completes. ; Begin Event ID = bullet_large_hit_done End ; ; Handler for the "effect_started" event ; This is the entry-point of the effect. ; Begin Handler EventID = $game.event.effect_started MatchObject = True Begin ; ; Work out the direction between ; the robot and the target so we ; can play the correct muzzle flash ; animation. ; *select_actor *arg !Anim:Position !Player:Position Push !Player:Position *select_object Push !Effect:TargetPosition Facing Push #!Anim:Facing ; ; Trigger the muzzle flash ; *arg !Anim:CompletionEventID $game.effect.bullet_large_fire.event.bullet_large_launch *arg !Anim:Target !selected *arg !Anim:Delay 10 *start_anim $game.animset.muzzle_flash 5 ; ; Make the game wait until the effect completes. ; *arg !Wait:WaitEventID $game.event.effect_completed Push 1 Wait End End
The next step is to play the actual bullet animation from the robot to the target.
Begin Handler EventID = $game.effect.bullet_large_fire.event.bullet_large_launch MatchObject = True Begin *select_actor *arg !Anim:Position !Player:Position *select_object *arg !Anim:TargetPosition !Effect:TargetPosition *arg !Anim:CompletionEventID $game.effect.bullet_large_fire.event.bullet_large_done *arg !Anim:Target !selected *start_anim $game.animset.bullet_large 4 End End
Then we decide whether the target has taken a hit.
Begin Handler EventID = $game.effect.bullet_large_fire.event.bullet_large_done MatchObject = True Begin ; ; Play a "splat" animation ; at the target position. ; *select_object *arg !Anim:Position !Effect:TargetPosition *arg !Anim:CompletionEventID $game.effect.bullet_large_fire.event.bullet_large_hit_done *arg !Anim:Target !selected *start_anim $game.anim.hit_1 3 ; ; Generate a "hit" on the target ; position. ; Push !Effect:TargetPosition Store !Map *arg !Hit:Target !Map:Occupant *arg !Hit:Type #hit_projectile *select_owner *arg !Hit:Strength $game.effect.bullet_large_fire.attribute.hit_strength *hit 3 End End
Finally, we emit an effect_completed event, signalling to the game that the effect is finished and the game can continue.
Begin Handler EventID = $game.effect.bullet_large_fire.event.bullet_large_hit_done MatchObject = True Begin *emit_event $game.event.effect_completed 0 End End
Circle Work
Ranged weapons all follow this pattern - from the piddly little Cutters the robots start with through to the heavy stuff like the Grenade Launcher. One place where the grenade launcher differs is what happens when the grenade clunks off the forehead of some unlucky dweeb. Rather than a simple hit, this one triggers a second effect - a $game.effect.explosion!
Explosions make use of an operation called an Area. This operation takes a radius and a set of octants and executes an event handler for each affected map position. Octants divide an area of the map into 45 degree segments that can be operated on with obstacle detection - this is used by the lighting and vision code. The Area operation makes this available to code running in the virtual machine.
This algorithm came from a really helpful blog post named What The Hero Sees by Bob Nystrom. I've implemented it as a C++ class called OctantMapEnumerator that can be used like this:
OctantMapEnumerator mapEnum; mapEnum.init(x, y, octants, radius); while (mapEnum.nextOctant()) { while (mapEnum.nextRow()) { while (mapEnum.nextTile()) { if (mapEnum.isVisible() == FALSE) { continue; } // Do something with current tile } } }
There are a number of other MapEnumerator classes with similar interfaces that are used for efficiently examining and manipulating the game map - RectMapEnumerator for rectangular areas, LineMapEnumerator for tracing a straight line between two tiles, AStarMapEnumerator for path finding an so on.
The result of the grenade launcher's Area Effect looks something like this:
Fire It Up!
Most of the weapons shown at the top of the post use this same pattern, just with different hit types and strengths, animations and sounds. One exception is the flamethrower, it isn't a ranged weapon. When the player uses the flamethrower, it triggers a "fire" effect that immediately blasts flames out in front of the robot.
The flames also use the Area operation. In this case, instead of being applied to all octants, it is only applied to those in the direction the robot is facing.
Thanks for reading, I hope you found this post interesting! I need to go and draw a better pulse rifle, I think.
Get Ecliptic
Ecliptic
Status | In development |
Author | bwldrbst |
Genre | Role Playing |
Tags | Amiga, Retro, Sci-fi, Turn-based |
Languages | English |
More posts
- September 2024 Demo Build56 days ago
- September 2024 Update58 days ago
- June 2024 UpdateJun 24, 2024
- Making MonstersApr 28, 2024
- April 2024 UpdateApr 06, 2024
Comments
Log in with itch.io to leave a comment.
Looks cool! Thanks for the detailed post.