1--[[
2  3omns - old-school arcade-style tile-based bomb-dropping deathmatch jam
3          <https://chazomaticus.github.io/3omns/>
4  base - base game logic for 3omns
5  Copyright 2014-2015 Charles Lindsay <chaz@chazomatic.us>
6
7  3omns is free software: you can redistribute it and/or modify it under the
8  terms of the GNU General Public License as published by the Free Software
9  Foundation, either version 3 of the License, or (at your option) any later
10  version.
11
12  3omns is distributed in the hope that it will be useful, but WITHOUT ANY
13  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License along with
17  3omns.  If not, see <http://www.gnu.org/licenses/>.
18]]
19
20local obj = require("object")
21local Entity = require("entities.entity")
22
23local Bomn = obj.class("Bomn", Entity)
24package.loaded[...] = Bomn
25
26local core = require("core")
27local util = require("util")
28local serial = require("serial")
29local Blast = require("sprites.blast")
30
31
32Bomn.TIME = 3
33Bomn.RADIUS = 8
34
35Bomn.ANIMATION = nil
36
37local function init_animation()
38  if Bomn.ANIMATION then return end
39
40  Bomn.ANIMATION = {
41    {time = 5.0,  image = core.IMAGES.BOMNS[5],                  z_order = 2},
42    {time = 4.75, image = nil,                                   z_order = 2},
43    {time = 4.5,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
44    {time = 4.25, image = nil,                                   z_order = 2},
45    {time = 4.0,  image = core.IMAGES.BOMNS[4],                  z_order = 2},
46    {time = 3.75, image = nil,                                   z_order = 2},
47    {time = 3.5,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
48    {time = 3.25, image = nil,                                   z_order = 2},
49    {time = 3.0,  image = core.IMAGES.BOMNS[3],                  z_order = 2},
50    {time = 2.75, image = nil,                                   z_order = 2},
51    {time = 2.5,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
52    {time = 2.25, image = nil,                                   z_order = 2},
53    {time = 2.0,  image = core.IMAGES.BOMNS[2],                  z_order = 2},
54    {time = 1.75, image = nil,                                   z_order = 2},
55    {time = 1.5,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
56    {time = 1.25, image = nil,                                   z_order = 2},
57    {time = 1.0,  image = core.IMAGES.BOMNS[1],                  z_order = 2},
58    {time = 0.75, image = nil,                                   z_order = 2},
59    {time = 0.5,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
60    {time = 0.4,  image = nil,                                   z_order = 2},
61    {time = 0.3,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
62    {time = 0.2,  image = nil,                                   z_order = 2},
63    {time = 0.1,  image = core.IMAGES.BOMNS[#core.IMAGES.BOMNS], z_order = 2},
64  }
65end
66
67function Bomn:init_base(entities, backing)
68  init_animation()
69
70  Entity.init_base(self, entities, backing)
71
72  self.solid = false
73  self.time = Bomn.TIME
74  self.dude_id = nil
75
76  self:animate(backing)
77end
78
79function Bomn:init(entities, pos, dude_id)
80  Entity.init(self, entities, pos, 1)
81
82  self.dude_id = dude_id
83end
84
85function Bomn:serialize()
86  return serial.serialize_number(self.time)
87      .. serial.serialize_number(self.dude_id)
88end
89
90function Bomn:sync(serialized, start, backing)
91  self.time, start = serial.deserialize_number(serialized, start)
92  self.dude_id, start = serial.deserialize_number(serialized, start)
93  return start
94end
95
96function Bomn:animate(backing)
97  local frame = util.get_animation_frame(Bomn.ANIMATION, self.time)
98  self:set_image(frame.image, backing)
99  self:set_z_order(frame.z_order, backing)
100end
101
102-- TODO: if the server deletes this entity before it explodes, we get no
103-- explosion locally (though the effect is exactly the same, since it does
104-- happen on the server).
105function Bomn:explode(backing)
106  self:kill(backing)
107
108  local blasts = {}
109  local to_blast = {}
110  util.circle_contig(self.pos, Bomn.RADIUS, function(edge_pos)
111    util.line(self.pos, edge_pos, function(pos)
112      if not self.entities:walkable(pos) then
113        return false
114      end
115
116      local key = core.pos_key(pos)
117      if not blasts[key] then
118        blasts[key] = pos
119      end
120
121      local other = self.entities:get_entity(pos)
122      if other and other.solid then
123        if not to_blast[key] then
124          to_blast[key] = other
125        end
126
127        return core.pos_equal(pos, self.pos)
128      end
129
130      return not core.pos_equal(pos, edge_pos)
131    end)
132  end)
133
134  for _, e in pairs(to_blast) do
135    e:blasted(self, backing)
136  end
137
138  for _, p in pairs(blasts) do
139    Blast(self.entities.level, p)
140  end
141end
142
143function Bomn:bumped(dude, dude_backing)
144  if self.dude_id ~= dude.id then
145    self:kill()
146  end
147  return true
148end
149
150function Bomn:l3_update(backing, elapsed)
151  self.time = self.time - elapsed
152  if self.time <= 0 then
153    self:explode(backing)
154    return
155  end
156
157  self:animate(backing)
158end
159