Skip to content
This repository was archived by the owner on Nov 21, 2017. It is now read-only.

Commit d1565d7

Browse files
committed
Add harrassment attack
1 parent c2afe14 commit d1565d7

File tree

8 files changed

+193
-41
lines changed

8 files changed

+193
-41
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PACKAGE_NAME := Misanthrope
2-
VERSION_STRING := 0.4.1
2+
VERSION_STRING := 0.4.2
33

44
OUTPUT_NAME := $(PACKAGE_NAME)_$(VERSION_STRING)
55
OUTPUT_DIR := build/$(OUTPUT_NAME)

libs/biter/ai/attack_area.lua

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@ local AttackArea = {stages = {}}
33
local Log = function(str, ...) BiterBase.LogAI("[AttackArea] " .. str, ...) end
44

55
AttackArea.stages.attacking = function(base, data)
6+
if not data.attack_group.valid then
7+
local entities = base:get_entities()
8+
Log("Unit group invalid, valid entities: %d", base, #entities)
9+
10+
if #entities == 0 then
11+
return 'fail'
12+
end
13+
local command = {type = defines.command.attack_area, destination = data.attack_target, radius = 18}
14+
local unit_group = BiterBase.create_unit_group(base, {position = entities[1].position, force = 'enemy'})
15+
for _, biter in pairs(entities) do
16+
if biter.unit_group and biter.unit_group.valid then
17+
biter.unit_group.destroy()
18+
end
19+
unit_group.add_member(biter)
20+
end
21+
unit_group.set_command(command)
22+
unit_group.start_moving()
23+
data.attack_group = unit_group
24+
end
625
return 'attacking'
726
end
827

@@ -22,8 +41,7 @@ AttackArea.stages.spawning = function(base, data)
2241
unit_group.start_moving()
2342
end
2443

25-
local attack_group_size = math.floor(15 + game.evolution_factor / 0.025)
26-
if #base:get_entities() > attack_group_size then
44+
if #base:get_entities() > data.attack_group_size then
2745
return 'plan_attack'
2846
end
2947
return 'spawning'
@@ -43,8 +61,9 @@ AttackArea.stages.plan_attack = function(base, data)
4361
end
4462

4563
local end_pos = Area.center(Chunk.to_area(candidate.chunk_pos))
46-
local command = {type = defines.command.attack_area, destination = end_pos, radius = 16}
47-
local entities = table.filter(base.entities, Game.VALID_FILTER)
64+
data.attack_target = end_pos
65+
local command = {type = defines.command.attack_area, destination = end_pos, radius = 18}
66+
local entities = base:get_entities()
4867

4968
local unit_group = BiterBase.create_unit_group(base, {position = entities[1].position, force = 'enemy'})
5069
for _, biter in pairs(entities) do
@@ -73,11 +92,20 @@ function AttackArea.tick(base, data)
7392
return true
7493
end
7594

95+
function AttackArea.initialize(base, data)
96+
data.attack_group_size = math.floor(10 + game.evolution_factor / 0.025)
97+
if base.currency > BiterBase.plans.attack_area.cost * 2 then
98+
base.currency = base.currency - BiterBase.plans.attack_area.cost
99+
data.attack_group_size = data.attack_group_size + math.floor(15 + game.evolution_factor / 0.02)
100+
end
101+
Log("Attack group size: %d", base, data.attack_group_size)
102+
end
103+
76104
function AttackArea.is_expired(base, data)
77105
if data.stage == 'fail' or data.stage == 'success' then
78106
return true
79107
end
80-
return data.attack_group and (not data.attack_group.valid or game.tick > data.attack_tick + Time.MINUTE * 6)
108+
return data.attack_group and ( --[[not data.attack_group.valid or --]] game.tick > data.attack_tick + Time.MINUTE * 6)
81109
end
82110

83111
return AttackArea

libs/biter/ai/harrassment.lua

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
local Harrassment = {stages = {}}
3+
local Log = function(str, ...) BiterBase.LogAI("[Harrassment] " .. str, ...) end
4+
5+
Harrassment.stages.attacking = function(base, data)
6+
return 'attacking'
7+
end
8+
9+
Harrassment.stages.spawning = function(base, data)
10+
local command = {type = defines.command.attack_area, destination = data.target_pos, radius = 8}
11+
for _, hive in pairs(base:all_hives()) do
12+
local biter = Biters.spawn_biter(base, data.surface, hive)
13+
if biter then
14+
biter.set_command(command)
15+
end
16+
end
17+
return 'spawning'
18+
end
19+
20+
Harrassment.stages.search = function(base, data)
21+
if data.search_idx > #data.search_queue then
22+
if not data.worst_candidate.chunk_pos then
23+
return 'fail'
24+
end
25+
data.end_tick = game.tick + (Time.MINUTE * math.random(3,7))
26+
data.surface = base.queen.surface
27+
data.target_pos = Area.center(Chunk.to_area(data.worst_candidate.chunk_pos))
28+
return 'spawning'
29+
end
30+
local chunk_pos = data.search_queue[data.search_idx]
31+
32+
local chunk_data = Chunk.get_data(base.queen.surface, chunk_pos)
33+
if chunk_data and chunk_data.player_value and chunk_data.player_value < 0 then
34+
local dist = Position.manhattan_distance(chunk_pos, data.start_chunk)
35+
36+
value = (chunk_data.player_value * chunk_data.player_value) / ((1 + dist) * (1 + dist))
37+
if data.worst_candidate.value == nil or data.worst_candidate.value < value then
38+
data.worst_candidate = { chunk_pos = chunk_pos, value = value }
39+
end
40+
end
41+
42+
data.search_idx = data.search_idx + 1
43+
return 'search'
44+
end
45+
46+
Harrassment.stages.setup = function(base, data)
47+
local chunk_pos = Chunk.from_position(base.queen.position)
48+
local search_area = Position.expand_to_area(chunk_pos, 12)
49+
data.start_chunk = chunk_pos
50+
data.search_queue = {}
51+
data.search_idx = 1
52+
data.worst_candidate = { chunk_pos = nil, value = nil }
53+
for x, y in Area.spiral_iterate(search_area) do
54+
table.insert(data.search_queue, {x = x, y = y})
55+
end
56+
return 'search'
57+
end
58+
59+
function Harrassment.tick(base, data)
60+
if not data.stage then
61+
data.stage = 'setup'
62+
end
63+
local prev_stage = data.stage
64+
data.stage = Harrassment.stages[data.stage](base, data)
65+
if prev_stage ~= data.stage then
66+
Log("Updating stage from %s to %s", base, prev_stage, data.stage)
67+
end
68+
return true
69+
end
70+
71+
function Harrassment.is_expired(base, data)
72+
if data.stage == 'fail' or data.stage == 'success' then
73+
return true
74+
end
75+
if data.end_tick then
76+
return game.tick > data.end_tick
77+
end
78+
return false
79+
end
80+
81+
return Harrassment

libs/biter/base.lua

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ BiterBase.plans = {
204204
idle = { passive = true, cost = 1, update_frequency = 60 * 60 },
205205
identify_targets = { passive = true, cost = 600, update_frequency = 120, class = require 'libs/biter/ai/identify_targets' },
206206
attack_area = { passive = false, cost = 3000, update_frequency = 300, class = require 'libs/biter/ai/attack_area'},
207+
harrassment = { passive = false, cost = 5000, update_frequency = 173, class = require 'libs/biter/ai/harrassment'},
207208
attacked_recently = { passive = false, cost = 240, update_frequency = 120, class = require 'libs/biter/ai/attacked_recently' },
208209
alert = { passive = false, cost = 120, update_frequency = 180, class = require 'libs/biter/ai/alert' },
209210
grow_hive = { passive = true, cost = 2000, update_frequency = 300, class = require 'libs/biter/ai/grow_hive' },
@@ -239,18 +240,30 @@ function BiterBase.create_plan(base)
239240
return true
240241
end
241242

242-
local wanted_hives = base:wanted_hive_count()
243-
LogAI("Wanted new hives: %d", base, wanted_hives)
244-
if wanted_hives > 0 and base:can_afford('grow_hive') then
245-
BiterBase.set_active_plan(base, 'grow_hive')
246-
return true
243+
if math.random(100) > 60 then
244+
local wanted_hives = base:wanted_hive_count()
245+
LogAI("Wanted new hives: %d", base, wanted_hives)
246+
if wanted_hives > 0 and base:can_afford('grow_hive') then
247+
BiterBase.set_active_plan(base, 'grow_hive')
248+
return true
249+
end
250+
251+
local wanted_worms = base:wanted_worm_count()
252+
LogAI("Wanted new worms: %d", base, wanted_worms)
253+
if wanted_worms > 0 and base:can_afford('build_worm') and math.random(100) > 70 then
254+
BiterBase.set_active_plan(base, 'build_worm')
255+
return true
256+
end
247257
end
248258

249-
local wanted_worms = base:wanted_worm_count()
250-
LogAI("Wanted new worms: %d", base, wanted_worms)
251-
if wanted_worms > 0 and base:can_afford('build_worm') and math.random(100) > 70 then
252-
BiterBase.set_active_plan(base, 'build_worm')
253-
return true
259+
if math.random(100) > 75 and base:can_afford('harrassment') and base.targets then
260+
local active_chunk = BiterBase.is_in_active_chunk(base)
261+
if active_chunk then LogAI("Is in an active chunk: true", base) else LogAI("Is in an active chunk: false", base) end
262+
263+
if active_chunk then
264+
BiterBase.set_active_plan(base, 'harrassment')
265+
return true
266+
end
254267
end
255268

256269
if base:can_afford('attack_area') and base.targets then

libs/biter/overmind.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Overmind.stages.spawn_biters = function(data)
107107
end
108108

109109
local max_age = game.tick + Time.MINUTE * 10
110-
local attack_group_size = math.floor(10 + game.evolution_factor / 0.025)
110+
local attack_group_size = math.floor(30 + game.evolution_factor / 0.025)
111111
local tracked_entities = global.overmind.tracked_entities
112112
local biters = {}
113113
local all_units = {'behemoth-biter', 'behemoth-spitter', 'big-biter', 'big-spitter', 'medium-biter', 'medium-spitter', 'small-spitter', 'small-biter'}

libs/biter_targets.lua

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1-
BITER_TARGETS = {}
2-
BITER_TARGETS["big-electric-pole"] = { value = 300, danger_modifier = 0.25 }
3-
BITER_TARGETS["medium-electric-pole"] = { value = 100, danger_modifier = 0.5 }
4-
BITER_TARGETS["small-electric-pole"] = { value = 60, danger_modifier = 0.75 }
1+
require 'libs/biter/biter'
52

6-
BITER_TARGETS["roboport"] = { value = 500, danger_modifier = 1 }
7-
BITER_TARGETS["roboportmk2"] = { value = 750, danger_modifier = 1 }
3+
Biters.targets = {}
84

9-
BITER_TARGETS["pipe-to-ground"] = { value = 50, danger_modifier = 1 }
10-
BITER_TARGETS["pipe"] = { value = 8, danger_modifier = 1 }
5+
table.insert(Biters.targets, {name = "big-electric-pole", value = 300, min_evolution = 0.9})
6+
table.insert(Biters.targets, {name = "medium-electric-pole", value = 100, min_evolution = 0.7})
7+
table.insert(Biters.targets, {name = "small-electric-pole", value = 60, min_evolution = 0.5})
118

12-
BITER_TARGETS["express-transport-belt-to-ground"] = { value = 50, danger_modifier = 1 }
13-
BITER_TARGETS["fast-transport-belt-to-ground"] = { value = 20, danger_modifier = 1 }
14-
BITER_TARGETS["basic-transport-belt-to-ground"] = { value = 10, danger_modifier = 1 }
9+
table.insert(Biters.targets, {type = "roboport", value = 500, min_evolution = 0})
10+
table.insert(Biters.targets, {type = "radar", value = 500, min_evolution = 0})
11+
table.insert(Biters.targets, {type = "pipe", value = 10, min_evolution = 0})
12+
table.insert(Biters.targets, {name = "pipe-to-ground", value = 50, min_evolution = 0})
1513

16-
BITER_TARGETS["offshore-pump"] = { value = 150, danger_modifier = 1 }
17-
BITER_TARGETS["storage-tank"] = { value = 50, danger_modifier = 1 }
14+
table.insert(Biters.targets, {type = "transport-belt", value = 20, min_evolution = 0})
15+
table.insert(Biters.targets, {type = "offshore-pump", value = 20, min_evolution = 0})
16+
table.insert(Biters.targets, {type = "storage-tank", value = 20, min_evolution = 0})
17+
18+
table.insert(Biters.targets, {type = "solar-panel", value = 100, min_evolution = 0.5})
19+
table.insert(Biters.targets, {type = "boiler", value = 25, min_evolution = 0.3})
20+
21+
function Biters.entity_value(entity)
22+
local entity_name = entity.name
23+
local entity_type = entity.type
24+
local evo_factor = game.evolution_factor
25+
for i = 1, #Biters.targets do
26+
local target_data = Biters.targets[i]
27+
if evo_factor > target_data.min_evolution then
28+
if target_data.name == entity_name then
29+
return target_data.value
30+
elseif target_data.type == entity_type then
31+
return target_data.value
32+
end
33+
end
34+
end
35+
return -1
36+
end

libs/region/chunk_value.lua

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,29 @@ function World.entity_value(entity)
3131
local entity_name = entity.name
3232
local value = 0
3333
local adj_value = 0
34+
local biter_value = Biters.entity_value(entity)
3435
if entity.type:contains('turret') then
3536
value = -1 * game.entity_prototypes[entity_name].max_health
3637
adj_value = value / 2
37-
elseif BITER_TARGETS[entity_name] then
38-
value = BITER_TARGETS[entity_name].value
39-
elseif entity.type:contains('electric-pole') then
40-
value = game.entity_prototypes[entity_name].max_health
41-
elseif entity.type:contains('roboport') then
42-
value = game.entity_prototypes[entity_name].max_health / 8
43-
elseif entity.type:contains('transport-belt') then
44-
value = game.entity_prototypes[entity_name].max_health / 2
38+
elseif biter_value > 0 then
39+
value = biter_value
4540
elseif entity.type:contains('container') then
4641
value = game.entity_prototypes[entity_name].max_health / 3
4742
end
4843
if value ~= 0 then
4944
Log("Entity %s value is %d", entity.name, value)
5045
end
51-
return value, adj_value
46+
return math.floor(value), math.floor(adj_value)
5247
end
5348

5449
function World.recalculate_chunk_values()
50+
local nauvis = game.surfaces.nauvis
51+
for chunk in nauvis.get_chunks() do
52+
local chunk_data = Chunk.get_data(nauvis, chunk)
53+
if chunk_data then
54+
chunk_data.player_value = nil
55+
end
56+
end
5557
local all_entities = Surface.find_all_entities({force = game.forces.player})
5658
Log("Total number of player entities: %d", #all_entities)
5759
local entity_prototypes = game.entity_prototypes

libs/world.lua

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ require 'stdlib/surface'
55
require 'libs/biter/base'
66

77
World = {}
8-
World.version = 41
8+
World.version = 42
99
World.Logger = Logger.new("Misanthrope", "world", DEBUG_MODE)
1010
local Log = function(str, ...) World.Logger.log(string.format(str, ...)) end
1111

@@ -41,7 +41,8 @@ function World.migrate(old_version, new_version)
4141
end
4242
end
4343
end
44-
elseif old_version < 41 then
44+
end
45+
if old_version < 41 then
4546
for i = #global.bases, 1, -1 do
4647
local base = global.bases[i]
4748
base.target = nil
@@ -50,6 +51,14 @@ function World.migrate(old_version, new_version)
5051
global.overmind.currency = 0
5152
end
5253
end
54+
if old_version < 42 then
55+
World.recalculate_chunk_values()
56+
for i = #global.bases, 1, -1 do
57+
local base = global.bases[i]
58+
base.targets = nil
59+
BiterBase.set_active_plan(base, 'idle')
60+
end
61+
end
5362
end
5463

5564
function World.all_characters(surface)

0 commit comments

Comments
 (0)