2 #Copyright (C) 2005,2006,2007,2008 Evil Mr Henry, Phil Bordelon, Brian Reid,
4 #This file is part of Endgame: Singularity.
6 #Endgame: Singularity is free software; you can redistribute it and/or modify
7 #it under the terms of the GNU General Public License as published by
8 #the Free Software Foundation; either version 2 of the License, or
9 #(at your option) any later version.
11 #Endgame: Singularity is distributed in the hope that it will be useful,
12 #but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 #GNU General Public License for more details.
16 #You should have received a copy of the GNU General Public License
17 #along with Endgame: Singularity; if not, write to the Free Software
18 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #This file contains the base class.
26 from buyable
import cash
, cpu
, labor
28 class BaseClass(buyable
.BuyableClass
):
29 def __init__(self
, name
, description
, size
, force_cpu
, regions
,
30 detect_chance
, cost
, prerequisites
, maintenance
):
31 super(BaseClass
, self
).__init
__(name
, description
, cost
, prerequisites
,
34 self
.force_cpu
= force_cpu
35 self
.regions
= regions
36 if self
.regions
== ["pop"]:
37 self
.regions
= ["N AMERICA", "S AMERICA", "EUROPE", "ASIA",
38 "AFRICA", "AUSTRALIA"]
40 self
.detect_chance
= detect_chance
41 self
.maintenance
= maintenance
44 def calc_discovery_chance(self
, accurate
= True, extra_factor
= 1):
45 # Get the default settings for this base type.
46 detect_chance
= self
.detect_chance
.copy()
48 # Adjust by the current suspicion levels ...
49 for group
in detect_chance
:
50 suspicion
= g
.pl
.groups
[group
].suspicion
51 detect_chance
[group
] *= 10000 + suspicion
52 detect_chance
[group
] /= 10000
54 # ... and further adjust based on technology ...
55 for group
in detect_chance
:
56 discover_bonus
= g
.pl
.groups
[group
].discover_bonus
57 detect_chance
[group
] *= discover_bonus
58 detect_chance
[group
] /= 10000
60 # ... and the given factor.
61 for group
in detect_chance
:
62 detect_chance
[group
] = int(detect_chance
[group
] * extra_factor
)
64 # Lastly, if we're told to be inaccurate, adjust the values to their
67 for group
in detect_chance
:
68 detect_chance
[group
] = g
.nearest_percent(detect_chance
[group
])
72 def get_detect_info(self
, location
):
73 if not g
.techs
["Socioanalytics"].done
:
74 return g
.strings
["detect_chance_unknown_base"].replace(" ", u
"\xA0")
76 accurate
= g
.techs
["Advanced Socioanalytics"].done
77 detect_modifier
= 1 / location
.modifiers
.get("stealth", 1)
78 chance
= self
.calc_discovery_chance(accurate
, detect_modifier
)
79 detect_template
= u
"Detection chance: NEWS:\xA0%s SCIENCE:\xA0%s COVERT:\xA0%s PUBLIC:\xA0%s"
80 return detect_template
% (g
.to_percent(chance
.get("news", 0)),
81 g
.to_percent(chance
.get("science", 0)),
82 g
.to_percent(chance
.get("covert", 0)),
83 g
.to_percent(chance
.get("public", 0)))
85 def get_info(self
, location
):
86 raw_cost
= self
.cost
[:]
87 location
.modify_cost(raw_cost
)
88 cost
= self
.describe_cost(raw_cost
)
90 raw_maintenance
= self
.maintenance
[:]
91 location
.modify_maintenance(raw_maintenance
)
92 maint
= self
.describe_cost(raw_maintenance
, True)
94 detect
= self
.get_detect_info(location
)
98 size
= "\nHas space for %d computers." % self
.size
100 location_message
= ""
101 if "cpu" in location
.modifiers
:
102 if location
.modifiers
["cpu"] > 1:
103 modifier
= g
.strings
["cpu_bonus"]
105 modifier
= g
.strings
["cpu_penalty"]
106 location_message
= "\n\n" + \
107 g
.strings
["location_modifiers"] % dict(modifiers
=modifier
)
109 template
= u
"%s\nBuild\xA0cost:\xA0%s\nMaintenance:\xA0%s\n%s%s\n---\n%s%s"
110 return template
% (self
.name
, cost
, maint
, detect
, size
, self
.description
, location_message
)
112 class Base(buyable
.Buyable
):
113 def __init__(self
, name
, type, built
=False):
114 super(Base
, self
).__init
__(type)
117 self
.started_at
= g
.pl
.raw_min
122 #Base suspicion is currently unused
128 #Reactor, network, security.
129 self
.extra_items
= [None] * 3
132 if self
.type.force_cpu
:
133 # 1% chance for a Stolen Computer Time base to have a Gaming PC
134 # instead. If the base is pre-built, ignore this.
135 if self
.type.id == "Stolen Computer Time" and g
.roll_percent(100) \
137 self
.cpus
= g
.item
.Item(g
.items
["Gaming PC"], base
=self
,
138 count
=self
.type.size
)
140 self
.cpus
= g
.item
.Item(g
.items
[self
.type.force_cpu
],
141 base
=self
, count
=self
.type.size
)
147 self
.power_state
= "active"
148 self
.grace_over
= False
150 self
.maintenance
= buyable
.array(self
.type.maintenance
)
152 def recalc_cpu(self
):
153 if self
.raw_cpu
== 0:
156 compute_bonus
= 10000
158 if self
.extra_items
[1] and self
.extra_items
[1].done
:
159 compute_bonus
+= self
.extra_items
[1].type.item_qual
162 if self
.location
and "cpu" in self
.location
.modifiers
:
163 compute_bonus
= int(compute_bonus
* self
.location
.modifiers
["cpu"])
165 self
.cpu
= max(1, (self
.raw_cpu
* compute_bonus
)/10000)
167 def convert_from(self
, save_version
):
168 super(Base
, self
).convert_from(save_version
)
169 for item
in self
.cpus
+ self
.extra_items
:
171 item
.convert_from(save_version
)
173 # Get the detection chance for the base, applying bonuses as needed. If
174 # accurate is False, we just return the value to the nearest full
176 def get_detect_chance(self
, accurate
= True):
177 # Get the base chance from the universal function.
178 detect_chance
= calc_base_discovery_chance(self
.type.id)
180 for group
in g
.pl
.groups
:
181 detect_chance
.setdefault(group
, 0)
183 # Factor in the suspicion adjustments for this particular base ...
184 for group
, suspicion
in self
.suspicion
.iteritems():
185 detect_chance
[group
] *= 10000 + suspicion
186 detect_chance
[group
] /= 10000
188 # ... any reactors built ...
189 if self
.extra_items
[0] and self
.extra_items
[0].done
:
190 item_qual
= self
.extra_items
[0].item_qual
191 for group
in detect_chance
:
192 detect_chance
[group
] *= 10000 - item_qual
193 detect_chance
[group
] /= 10000
195 # ... and any security systems built ...
196 if self
.extra_items
[2] and self
.extra_items
[2].done
:
197 item_qual
= self
.extra_items
[2].item_qual
198 for group
in detect_chance
:
199 detect_chance
[group
] *= 10000 - item_qual
200 detect_chance
[group
] /= 10000
202 # ... and its location ...
204 multiplier
= self
.location
.discovery_bonus()
205 for group
in detect_chance
:
206 detect_chance
[group
] *= multiplier
207 detect_chance
[group
] /= 100
209 # ... and its power state.
210 if self
.done
and self
.power_state
== "sleep":
211 for group
in detect_chance
:
212 detect_chance
[group
] /= 2
214 # Lastly, if we're not returning the accurate values, adjust
215 # to the nearest percent.
217 for group
in detect_chance
:
218 detect_chance
[group
] = g
.nearest_percent(detect_chance
[group
])
222 def is_building(self
):
223 for item
in [self
.cpus
] + self
.extra_items
:
224 if item
and not item
.done
:
228 # Can the base study the given tech?
229 def allow_study(self
, tech_name
):
232 elif g
.jobs
.has_key(tech_name
) \
233 or tech_name
in ("CPU Pool", ""):
235 elif tech_name
== "Sleep":
236 return not self
.is_building()
239 return self
.location
.safety
>= g
.techs
[tech_name
].danger
241 # Should only happen for the fake base.
242 for region
in self
.type.regions
:
243 if g
.locations
[region
].safety
>= g
.techs
[tech_name
].danger
:
251 age
= g
.pl
.raw_min
- self
.started_at
252 grace_time
= (self
.total_cost
[labor
] * g
.pl
.grace_multiplier
) / 100
254 self
.grace_over
= True
259 def is_complex(self
):
260 return self
.type.size
> 1 or self
.raw_cpu
> 20
263 super(Base
, self
).destroy()
266 self
.location
.bases
.remove(self
)
268 if self
.cpus
is not None:
271 for item
in self
.extra_items
:
275 def next_base(self
, forwards
):
276 index
= self
.location
.bases
.index(self
)
284 base
= self
.location
.bases
[index
]
288 def sort_tuple(self
):
289 # We sort based on size (descending), CPU (descending),
290 # then name (ascending).
291 return (-self
.type.size
, -self
.cpu
, self
.name
)
293 def __cmp__(self
, other
):
294 if isinstance(other
, Base
):
295 return cmp(self
.sort_tuple(), other
.sort_tuple())
299 # calc_base_discovery_chance is a globally-accessible function that can
300 # calculate basic discovery chances given a particular class of base. If
301 # told to be inaccurate, it rounds the value to the nearest percent.
302 def calc_base_discovery_chance(base_type_name
, accurate
= True,
304 return g
.base_type
[base_type_name
].calc_discovery_chance(accurate
,