7 changed files with 356 additions and 306 deletions
			
			
		- 
					2conf.lua
- 
					291data/towers.lua
- 
					1main.lua
- 
					8src/extra.lua
- 
					23src/game.lua
- 
					4src/map-editor.lua
- 
					333src/tower.lua
| @ -0,0 +1,291 @@ | |||
| 
 | |||
| --[[ | |||
|     the following is a list of tower specifications, which are declarations of a variety of properties describing what a tower is, and how it functions | |||
|     this a lua file. a quick run-down of what writing code in lua looks like: https://www.amulet.xyz/doc/#lua-primer | |||
| 
 | |||
|     each tower spec is a lua table. lua tables are the thing that you use to represent bundles of data (both arrays and hashtables are represented by tables) | |||
| 
 | |||
|     the format of the bundles in our case are described below. | |||
|     some propreties are optional. required properties are marked with an asterisk (*), and are generally included at the top of the list. | |||
| 
 | |||
|     # TOWER SPEC TABLE | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
|     | property name, required*  | datatype | general description / details                                  | | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
|     | name*                     | string   | exact one-line display name text of the tower                  | | |||
|     | placement_rules_text*     | string   | one-line description of the placement rules for this tower     | | |||
|     | short_description*        | string   | one-line description of the nature of this tower               | | |||
|     | texture*                  | userdata | @TODO                                                          | | |||
|     | icon_texture*             | userdata | @TODO                                                          | | |||
|     | cost*                     | number   | the starting cost of placing this tower                        | | |||
|     |                           |          |                                                                | | |||
|     | weapons*                  | table    | an array of weapons.                                           | | |||
|     |                           |          | order matters - two weapons share a 'choke' value, and both    | | |||
|     |                           |          | could acquire a target in a frame, the first one is choosen.   | | |||
|     |                           |          |                                                                | | |||
|     | placement_rules           | table    | @TODO                                                          | | |||
|     |                           |          |                                                                | | |||
|     |                           |          |                                                                | | |||
|     |                           |          |                                                                | | |||
|     |                           |          |                                                                | | |||
|     |                           |          |                                                                | | |||
|     |                           |          |                                                                | | |||
|     | update_f                  | function | default value is complicated @TODO                             | | |||
|     | grow_f                    | function | default value is false/nil. @TODO                              | | |||
|     | size                      | number   | default value of 1, which means the tower occupies one hex.    | | |||
|     | height                    | number   | default value of 1. height is relevant for mob pathing and     | | |||
|     |                           |          | projectile collision                                           | | |||
|     |                           |          |                                                                | | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
| 
 | |||
|     # WEAPON TABLE | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
|     | property name, required*  | datatype | general description / details                                  | | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
|     | type                      | number   | sometimes, instead of specifying everything for a weapon, it's | | |||
|     |                           |          | convenient to refer to a base type. if this is provided all of | | |||
|     |                           |          | the weapon's other fields will be initialized to preset values | | |||
|     |                           |          | and any other values you provide with the weapon spec will     | | |||
|     |                           |          | overwrite those preset values.                                 | | |||
|     |                           |          | if you provide a value here, all other properties become       | | |||
|     |                           |          | optional.                                                      | | |||
|     |                           |          | values you can provide, and what they mean:                    | | |||
|     |                           |          |   @TODO                                                        | | |||
|     |                           |          |                                                                | | |||
|     | fire_rate*                | number   | 'shots' per second, if the weapon has a valid target           | | |||
|     | range*                    | number   | max distance (in hexes) at which this weapon acquires targets  | | |||
|     |                           |          |                                                                | | |||
|     | min-range                 | number   | default of 0. min distance (in hexes) at which this weapon acquires targets  | | |||
|     | target_acquisition_f      | function | default value is complicated @TODO                             | | |||
|     | choke                     | number   | default of false/nil. @TODO                                    | | |||
|     |                           |          |                                                                | | |||
|     | --------------------------| -------- | -------------------------------------------------------------- | | |||
| ]] | |||
| 
 | |||
| return { | |||
|     { | |||
|         name = "Wall", | |||
|         placement_rules_text = "Place on Ground", | |||
|         short_description = "Restricts movement, similar to a mountain.", | |||
|         texture = TEXTURES.TOWER_WALL, | |||
|         icon_texture = TEXTURES.TOWER_WALL_ICON, | |||
|         cost = 10, | |||
|         range = 0, | |||
|         fire_rate = 2, | |||
|         update = false, | |||
|         placement_rules = { | |||
|         } | |||
|     }, | |||
|     { | |||
|         name = "Gattler", | |||
|         placement_rules_text = "Place on Ground", | |||
|         short_description = "Short-range, fast-fire rate single-target tower.", | |||
|         texture = TEXTURES.TOWER_GATTLER, | |||
|         icon_texture = TEXTURES.TOWER_GATTLER_ICON, | |||
|         cost = 20, | |||
|         range = 4, | |||
|         fire_rate = 0.5, | |||
|         update = function(tower, tower_index) | |||
|             if not tower.target_index then | |||
|                 -- we should try and acquire a target | |||
|                 for index,mob in pairs(game_state.mobs) do | |||
|                     if mob then | |||
|                         local d = math.distance(mob.hex, tower.hex) | |||
|                         if d <= tower.range then | |||
|                             tower.target_index = index | |||
|                             break | |||
|                         end | |||
|                     end | |||
|                 end | |||
| 
 | |||
|                 -- passive animation | |||
|                 tower.node("rotate").angle = math.wrapf(tower.node("rotate").angle + 0.1 * am.delta_time, math.pi*2) | |||
|             else | |||
|                 -- should have a target, so we should try and shoot it | |||
|                 if not game_state.mobs[tower.target_index] then | |||
|                     -- the target we have was invalidated | |||
|                     tower.target_index = false | |||
| 
 | |||
|                 else | |||
|                     -- the target we have is valid | |||
|                     local mob = game_state.mobs[tower.target_index] | |||
|                     local vector = math.normalize(mob.position - tower.position) | |||
| 
 | |||
|                     if (game_state.time - tower.last_shot_time) > tower.fire_rate then | |||
|                         local projectile = make_and_register_projectile( | |||
|                             tower.hex, | |||
|                             PROJECTILE_TYPE.BULLET, | |||
|                             vector | |||
|                         ) | |||
| 
 | |||
|                         tower.last_shot_time = game_state.time | |||
|                         play_sfx(SOUNDS.HIT1) | |||
|                     end | |||
| 
 | |||
|                     -- point the cannon at the dude | |||
|                     local theta = math.rad(90) - math.atan((tower.position.y - mob.position.y)/(tower.position.x - mob.position.x)) | |||
|                     local diff = tower.node("rotate").angle - theta | |||
| 
 | |||
|                     tower.node("rotate").angle = -theta + math.pi/2 | |||
|                 end | |||
|             end | |||
|         end | |||
|     }, | |||
|     { | |||
|         name = "Howitzer", | |||
|         placement_rules_text = "Place on Ground, with a 1 space gap between other towers and mountains - walls/moats don't count.", | |||
|         short_description = "Medium-range, medium fire-rate area of effect artillery tower.", | |||
|         texture = TEXTURES.TOWER_HOWITZER, | |||
|         icon_texture = TEXTURES.TOWER_HOWITZER_ICON, | |||
|         cost = 50, | |||
|         range = 6, | |||
|         fire_rate = 4, | |||
|         update = function(tower, tower_index) | |||
|             if not tower.target_index then | |||
|                 -- we don't have a target | |||
|                 for index,mob in pairs(game_state.mobs) do | |||
|                     if mob then | |||
|                         local d = math.distance(mob.hex, tower.hex) | |||
|                         if d <= tower.range then | |||
|                             tower.target_index = index | |||
|                             break | |||
|                         end | |||
|                     end | |||
|                 end | |||
| 
 | |||
|                 -- passive animation | |||
|                 tower.node("rotate").angle = math.wrapf(tower.node("rotate").angle + 0.1 * am.delta_time, math.pi*2) | |||
|             else | |||
|                 -- we should have a target | |||
|                 -- @NOTE don't compare to false, empty indexes appear on game reload | |||
|                 if not game_state.mobs[tower.target_index] then | |||
|                     -- the target we have was invalidated | |||
|                     tower.target_index = false | |||
| 
 | |||
|                 else | |||
|                     -- the target we have is valid | |||
|                     local mob = game_state.mobs[tower.target_index] | |||
|                     local vector = math.normalize(mob.position - tower.position) | |||
| 
 | |||
|                     if (game_state.time - tower.last_shot_time) > tower.fire_rate then | |||
|                         local projectile = make_and_register_projectile( | |||
|                             tower.hex, | |||
|                             PROJECTILE_TYPE.SHELL, | |||
|                             vector | |||
|                         ) | |||
| 
 | |||
|                         -- @HACK, the projectile will explode if it encounters something taller than it, | |||
|                         -- but the tower it spawns on quickly becomes taller than it, so we just pad it | |||
|                         -- if it's not enough the shell explodes before it leaves its spawning hex | |||
|                         projectile.props.z = tower.props.z + 0.1 | |||
| 
 | |||
|                         tower.last_shot_time = game_state.time | |||
|                         play_sfx(SOUNDS.EXPLOSION2) | |||
|                     end | |||
| 
 | |||
|                     -- point the cannon at the dude | |||
|                     local theta = math.rad(90) - math.atan((tower.position.y - mob.position.y)/(tower.position.x - mob.position.x)) | |||
|                     local diff = tower.node("rotate").angle - theta | |||
| 
 | |||
|                     tower.node("rotate").angle = -theta + math.pi/2 | |||
|                 end | |||
|             end | |||
|         end | |||
|     }, | |||
|     { | |||
|         name = "Redeye", | |||
|         placement_rules_text = "Place on Mountains.", | |||
|         short_description = "Long-range, penetrating high-velocity laser tower.", | |||
|         texture = TEXTURES.TOWER_REDEYE, | |||
|         icon_texture = TEXTURES.TOWER_REDEYE_ICON, | |||
|         cost = 75, | |||
|         range = 9, | |||
|         fire_rate = 3, | |||
|         update = function(tower, tower_index) | |||
|             if not tower.target_index then | |||
|                 for index,mob in pairs(game_state.mobs) do | |||
|                     if mob then | |||
|                         local d = math.distance(mob.hex, tower.hex) | |||
|                         if d <= tower.range then | |||
|                             tower.target_index = index | |||
|                             break | |||
|                         end | |||
|                     end | |||
|                 end | |||
|             else | |||
|                 if not game_state.mobs[tower.target_index] then | |||
|                     tower.target_index = false | |||
| 
 | |||
|                 elseif (game_state.time - tower.last_shot_time) > tower.fire_rate then | |||
|                     local mob = game_state.mobs[tower.target_index] | |||
| 
 | |||
|                     make_and_register_projectile( | |||
|                         tower.hex, | |||
|                         PROJECTILE_TYPE.LASER, | |||
|                         math.normalize(mob.position - tower.position) | |||
|                     ) | |||
| 
 | |||
|                     tower.last_shot_time = game_state.time | |||
|                     vplay_sfx(SOUNDS.LASER2) | |||
|                 end | |||
|             end | |||
|         end | |||
|     }, | |||
|     { | |||
|         name = "Moat", | |||
|         placement_rules_text = "Place on Ground", | |||
|         short_description = "Restricts movement, similar to water.", | |||
|         texture = TEXTURES.TOWER_MOAT, | |||
|         icon_texture = TEXTURES.TOWER_MOAT_ICON, | |||
|         cost = 10, | |||
|         range = 0, | |||
|         fire_rate = 2, | |||
|         height = -1, | |||
|         update = false | |||
|     }, | |||
|     { | |||
|         name = "Radar", | |||
|         placement_rules_text = "n/a", | |||
|         short_description = "Doesn't do anything right now :(", | |||
|         texture = TEXTURES.TOWER_RADAR, | |||
|         icon_texture = TEXTURES.TOWER_RADAR_ICON, | |||
|         cost = 100, | |||
|         range = 0, | |||
|         fire_rate = 1, | |||
|         update = false | |||
|     }, | |||
|     { | |||
|         name = "Lighthouse", | |||
|         placement_rules_text = "Place on Ground, adjacent to Water or Moats", | |||
|         short_description = "Attracts nearby mobs; temporarily redirects their path", | |||
|         texture = TEXTURES.TOWER_LIGHTHOUSE, | |||
|         icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON, | |||
|         cost = 150, | |||
|         range = 7, | |||
|         fire_rate = 1, | |||
|         target_aquisition_f = function(tower, tower_index) | |||
| 
 | |||
|         end, | |||
|         update = function(tower, tower_index) | |||
|             -- check if there's a mob on a hex in our perimeter | |||
|             for _,h in pairs(tower.perimeter) do | |||
|                 local mobs = mobs_on_hex(h) | |||
| 
 | |||
|                 for _,m in pairs(mobs) do | |||
|                     if not m.path and not m.seen_lighthouse then | |||
|                         -- @TODO only attract the mob if its frame target (direction vector) | |||
|                         -- is within some angle range...? if the mob is heading directly away from the tower, then | |||
|                         -- the lighthouse shouldn't do much | |||
| 
 | |||
|                         local path, made_it = hex_Astar(game_state.map, tower.hex, m.hex, grid_neighbours, grid_cost, grid_heuristic) | |||
| 
 | |||
|                         if made_it then | |||
|                             m.path = path | |||
|                             m.seen_lighthouse = true -- right now mobs don't care about lighthouses if they've already seen one. | |||
|                         end | |||
|                     end | |||
|                 end | |||
|             end | |||
|         end | |||
|     }, | |||
| } | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue