2010-08-08 10:06:34 +02:00
/*
Copyright ( c ) 1998 - 2010 , Enno Rehling < enno @ eressea . de >
Katja Zedel < katze @ felidae . kn - bremen . de
Christian Schlittchen < corwin @ amber . kn - bremen . de >
Permission to use , copy , modify , and / or distribute this software for any
purpose with or without fee is hereby granted , provided that the above
copyright notice and this permission notice appear in all copies .
THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
* */
# include <platform.h>
# include <kernel/config.h>
# include "build.h"
/* kernel includes */
# include "alchemy.h"
# include "alliance.h"
# include "connection.h"
# include "building.h"
# include "curse.h"
# include "faction.h"
# include "group.h"
# include "item.h"
# include "magic.h"
# include "message.h"
# include "move.h"
# include "order.h"
# include "pool.h"
# include "race.h"
# include "region.h"
# include "ship.h"
# include "skill.h"
# include "terrain.h"
# include "terrainid.h"
# include "unit.h"
/* from libutil */
# include <util/attrib.h>
# include <util/base36.h>
# include <util/event.h>
# include <util/goodies.h>
# include <util/language.h>
# include <util/log.h>
# include <util/parser.h>
# include <util/resolve.h>
# include <util/xml.h>
/* from libc */
# include <assert.h>
# include <limits.h>
# include <math.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
/* attributes inclues */
# include <attributes/matmod.h>
# include <attributes/alliance.h>
# define STONERECYCLE 50
/* Name, MaxGroesse, MinBauTalent, Kapazitaet, {Eisen, Holz, Stein, BauSilber,
* Laen , Mallorn } , UnterSilber , UnterSpezialTyp , UnterSpezial */
2011-03-07 08:02:35 +01:00
static boolean CheckOverload ( void )
2010-08-08 10:06:34 +02:00
{
static int value = - 1 ;
2011-03-07 08:02:35 +01:00
if ( value < 0 ) {
2010-08-08 10:06:34 +02:00
value = get_param_int ( global . parameters , " rules.check_overload " , 0 ) ;
}
return value ;
}
/* test if the unit can slip through a siege undetected.
* returns 0 if siege is successful , or 1 if the building is either
* not besieged or the unit can slip through the siege due to better stealth .
*/
2011-03-07 08:02:35 +01:00
static int slipthru ( const region * r , const unit * u , const building * b )
2010-08-08 10:06:34 +02:00
{
unit * u2 ;
int n , o ;
/* b ist die burg, in die man hinein oder aus der man heraus will. */
2011-03-07 08:02:35 +01:00
if ( b = = NULL | | b - > besieged < b - > size * SIEGEFACTOR ) {
2010-08-08 10:06:34 +02:00
return 1 ;
}
/* u wird am hinein- oder herausschluepfen gehindert, wenn STEALTH <=
* OBSERVATION + 2 der belagerer u2 ist */
n = eff_skill ( u , SK_STEALTH , r ) ;
for ( u2 = r - > units ; u2 ; u2 = u2 - > next ) {
if ( usiege ( u2 ) = = b ) {
2011-03-07 08:02:35 +01:00
if ( invisible ( u , u2 ) > = u - > number )
continue ;
2010-08-08 10:06:34 +02:00
o = eff_skill ( u2 , SK_PERCEPTION , r ) ;
if ( o + 2 > = n ) {
2011-03-07 08:02:35 +01:00
return 0 ; /* entdeckt! */
2010-08-08 10:06:34 +02:00
}
}
}
return 1 ;
}
2012-05-17 09:13:30 +02:00
int can_contact ( const region * r , const unit * u , const unit * u2 )
2010-08-08 10:06:34 +02:00
{
/* hier geht es nur um die belagerung von burgen */
2012-05-17 09:13:30 +02:00
if ( u - > building = = u2 - > building ) {
return 1 ;
}
2010-08-08 10:06:34 +02:00
/* unit u is trying to contact u2 - unasked for contact. wenn u oder u2
* nicht in einer burg ist , oder die burg nicht belagert ist , ist
* slipthru ( ) = = 1. ansonsten ist es nur 1 , wenn man die belagerer */
2012-05-17 09:13:30 +02:00
if ( slipthru ( u - > region , u , u - > building ) & & slipthru ( u - > region , u2 , u2 - > building ) ) {
return 1 ;
}
2010-08-08 10:06:34 +02:00
2012-05-17 09:13:30 +02:00
return ( alliedunit ( u , u2 - > faction , HELP_GIVE ) ) ;
2010-08-08 10:06:34 +02:00
}
2012-05-17 09:13:30 +02:00
static void contact_cmd ( unit * u , order * ord , int final )
2010-08-08 10:06:34 +02:00
{
/* unit u kontaktiert unit u2. Dies setzt den contact einfach auf 1 -
* ein richtiger toggle ist ( noch ? ) nicht noetig . die region als
* parameter ist nur deswegen wichtig , weil er an getunit ( )
* weitergegeben wird . dies wird fuer das auffinden von tempunits in
* getnewunit ( ) verwendet ! */
unit * u2 ;
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
2010-08-08 10:06:34 +02:00
init_tokens ( ord ) ;
skip_token ( ) ;
u2 = getunitg ( r , u - > faction ) ;
2011-03-07 08:02:35 +01:00
if ( u2 ! = NULL ) {
2010-08-08 10:06:34 +02:00
if ( ! can_contact ( r , u , u2 ) ) {
2012-05-17 09:13:30 +02:00
if ( final )
2011-03-07 08:02:35 +01:00
cmistake ( u , u - > thisorder , 23 , MSG_EVENT ) ;
2010-08-08 10:06:34 +02:00
return ;
}
usetcontact ( u , u2 ) ;
}
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
struct building * getbuilding ( const struct region * r )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
building * b = findbuilding ( getid ( ) ) ;
if ( b = = NULL | | r ! = b - > region )
return NULL ;
2010-08-08 10:06:34 +02:00
return b ;
}
2011-03-07 08:02:35 +01:00
ship * getship ( const struct region * r )
2010-08-08 10:06:34 +02:00
{
ship * sh , * sx = findship ( getshipid ( ) ) ;
for ( sh = r - > ships ; sh ; sh = sh - > next ) {
2011-03-07 08:02:35 +01:00
if ( sh = = sx )
return sh ;
2010-08-08 10:06:34 +02:00
}
return NULL ;
}
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static void siege_cmd ( unit * u , order * ord )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
2010-08-08 10:06:34 +02:00
building * b ;
int d , pooled ;
int bewaffnete , katapultiere = 0 ;
static boolean init = false ;
2011-03-07 08:02:35 +01:00
static const curse_type * magicwalls_ct ;
static item_type * it_catapultammo = NULL ;
static item_type * it_catapult = NULL ;
2010-08-08 10:06:34 +02:00
if ( ! init ) {
init = true ;
magicwalls_ct = ct_find ( " magicwalls " ) ;
it_catapultammo = it_find ( " catapultammo " ) ;
it_catapult = it_find ( " catapult " ) ;
}
/* gibt es ueberhaupt Burgen? */
init_tokens ( ord ) ;
skip_token ( ) ;
b = getbuilding ( r ) ;
if ( ! b ) {
cmistake ( u , ord , 31 , MSG_BATTLE ) ;
return ;
}
if ( ! playerrace ( u - > race ) ) {
/* keine Drachen, Illusionen, Untote etc */
cmistake ( u , ord , 166 , MSG_BATTLE ) ;
return ;
}
/* schaden durch katapulte */
d = i_get ( u - > items , it_catapult ) ;
d = MIN ( u - > number , d ) ;
pooled = get_pooled ( u , it_catapultammo - > rtype , GET_DEFAULT , d ) ;
d = MIN ( pooled , d ) ;
if ( eff_skill ( u , SK_CATAPULT , r ) > = 1 ) {
katapultiere = d ;
d * = eff_skill ( u , SK_CATAPULT , r ) ;
} else {
d = 0 ;
}
bewaffnete = armedmen ( u , true ) ;
if ( d = = 0 & & bewaffnete = = 0 ) {
/* abbruch, falls unbewaffnet oder unfaehig, katapulte zu benutzen */
cmistake ( u , ord , 80 , MSG_EVENT ) ;
return ;
}
if ( ! is_guard ( u , GUARD_TRAVELTHRU ) ) {
/* abbruch, wenn die einheit nicht vorher die region bewacht - als
2011-03-07 08:02:35 +01:00
* warnung fuer alle anderen ! */
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 81 , MSG_EVENT ) ;
return ;
}
/* einheit und burg markieren - spart zeit beim behandeln der einheiten
2011-03-07 08:02:35 +01:00
* in der burg , falls die burg auch markiert ist und nicht alle
* einheiten wieder abgesucht werden muessen ! */
2010-08-08 10:06:34 +02:00
usetsiege ( u , b ) ;
b - > besieged + = MAX ( bewaffnete , katapultiere ) ;
/* definitiver schaden eingeschraenkt */
d = MIN ( d , b - > size - 1 ) ;
/* meldung, schaden anrichten */
if ( d & & ! curse_active ( get_curse ( b - > attribs , magicwalls_ct ) ) ) {
b - > size - = d ;
2011-03-07 08:02:35 +01:00
use_pooled ( u , it_catapultammo - > rtype ,
GET_SLACK | GET_RESERVE | GET_POOLED_SLACK , d ) ;
2010-08-08 10:06:34 +02:00
/* send message to the entire region */
ADDMSG ( & r - > msgs , msg_message ( " siege_catapults " ,
2011-03-07 08:02:35 +01:00
" unit building destruction " , u , b , d ) ) ;
2010-08-08 10:06:34 +02:00
} else {
/* send message to the entire region */
2011-03-07 08:02:35 +01:00
ADDMSG ( & r - > msgs , msg_message ( " siege " , " unit building " , u , b ) ) ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
void do_siege ( region * r )
2010-08-08 10:06:34 +02:00
{
if ( fval ( r - > terrain , LAND_REGION ) ) {
unit * u ;
for ( u = r - > units ; u ; u = u - > next ) {
if ( get_keyword ( u - > thisorder ) = = K_BESIEGE ) {
siege_cmd ( u , u - > thisorder ) ;
}
}
}
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static void destroy_road ( unit * u , int nmax , struct order * ord )
2010-08-08 10:06:34 +02:00
{
direction_t d = getdirection ( u - > faction - > locale ) ;
unit * u2 ;
region * r = u - > region ;
short n = ( short ) nmax ;
2011-03-07 08:02:35 +01:00
if ( nmax > SHRT_MAX )
n = SHRT_MAX ;
else if ( nmax < 0 )
n = 0 ;
2010-08-08 10:06:34 +02:00
2011-03-07 08:02:35 +01:00
for ( u2 = r - > units ; u2 ; u2 = u2 - > next ) {
if ( u2 - > faction ! = u - > faction & & is_guard ( u2 , GUARD_TAX )
2010-08-08 10:06:34 +02:00
& & cansee ( u2 - > faction , u - > region , u , 0 )
& & ! alliedunit ( u , u2 - > faction , HELP_GUARD ) ) {
cmistake ( u , ord , 70 , MSG_EVENT ) ;
return ;
}
}
2011-03-07 08:02:35 +01:00
if ( d = = NODIRECTION ) {
2010-08-08 10:06:34 +02:00
/* Die Richtung wurde nicht erkannt */
cmistake ( u , ord , 71 , MSG_PRODUCE ) ;
} else {
short road = rroad ( r , d ) ;
n = MIN ( n , road ) ;
2011-03-07 08:02:35 +01:00
if ( n ! = 0 ) {
region * r2 = rconnect ( r , d ) ;
int willdo = eff_skill ( u , SK_ROAD_BUILDING , r ) * u - > number ;
2010-08-08 10:06:34 +02:00
willdo = MIN ( willdo , n ) ;
2011-03-07 08:02:35 +01:00
if ( willdo = = 0 ) {
2010-08-08 10:06:34 +02:00
/* TODO: error message */
}
2011-03-07 08:02:35 +01:00
if ( willdo > SHRT_MAX )
road = 0 ;
else
road = road - ( short ) willdo ;
2010-08-08 10:06:34 +02:00
rsetroad ( r , d , road ) ;
ADDMSG ( & u - > faction - > msgs , msg_message ( " destroy_road " ,
2011-03-07 08:02:35 +01:00
" unit from to " , u , r , r2 ) ) ;
2010-08-08 10:06:34 +02:00
}
}
}
2011-03-07 08:02:35 +01:00
int destroy_cmd ( unit * u , struct order * ord )
2010-08-08 10:06:34 +02:00
{
ship * sh ;
unit * u2 ;
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
const construction * con = NULL ;
2010-08-08 10:06:34 +02:00
int size = 0 ;
const char * s ;
int n = INT_MAX ;
if ( u - > number < 1 )
return 0 ;
init_tokens ( ord ) ;
skip_token ( ) ;
s = getstrtoken ( ) ;
2011-03-07 08:02:35 +01:00
if ( findparam ( s , u - > faction - > locale ) = = P_ROAD ) {
2010-08-08 10:06:34 +02:00
destroy_road ( u , INT_MAX , ord ) ;
return 0 ;
}
if ( s & & * s ) {
n = atoi ( ( const char * ) s ) ;
if ( n < = 0 ) {
cmistake ( u , ord , 288 , MSG_PRODUCE ) ;
return 0 ;
}
}
if ( getparam ( u - > faction - > locale ) = = P_ROAD ) {
destroy_road ( u , n , ord ) ;
return 0 ;
}
if ( u - > building ) {
building * b = u - > building ;
2012-05-18 00:23:15 +02:00
if ( u ! = building_owner ( b ) ) {
cmistake ( u , ord , 138 , MSG_PRODUCE ) ;
return 0 ;
}
2010-10-18 02:24:10 +02:00
if ( fval ( b - > type , BTF_INDESTRUCTIBLE ) ) {
cmistake ( u , ord , 138 , MSG_PRODUCE ) ;
return 0 ;
2011-03-07 08:02:35 +01:00
}
if ( n > = b - > size ) {
2010-08-08 10:06:34 +02:00
/* destroy completly */
/* all units leave the building */
for ( u2 = r - > units ; u2 ; u2 = u2 - > next ) {
if ( u2 - > building = = b ) {
freset ( u2 , UFL_OWNER ) ;
2012-05-18 00:23:15 +02:00
leave_building ( u2 ) ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_message ( " destroy " , " building unit " , b , u ) ) ;
2010-08-08 10:06:34 +02:00
con = b - > type - > construction ;
remove_building ( & r - > buildings , b ) ;
} else {
/* partial destroy */
b - > size - = n ;
ADDMSG ( & u - > faction - > msgs , msg_message ( " destroy_partial " ,
2011-03-07 08:02:35 +01:00
" building unit " , b , u ) ) ;
2010-08-08 10:06:34 +02:00
}
} else if ( u - > ship ) {
sh = u - > ship ;
2012-05-18 00:23:15 +02:00
if ( u ! = ship_owner ( sh ) ) {
cmistake ( u , ord , 138 , MSG_PRODUCE ) ;
return 0 ;
}
2010-08-08 10:06:34 +02:00
if ( fval ( r - > terrain , SEA_REGION ) ) {
cmistake ( u , ord , 14 , MSG_EVENT ) ;
return 0 ;
}
2011-03-07 08:02:35 +01:00
if ( n > = ( sh - > size * 100 ) / sh - > type - > construction - > maxsize ) {
2010-08-08 10:06:34 +02:00
/* destroy completly */
/* all units leave the ship */
for ( u2 = r - > units ; u2 ; u2 = u2 - > next ) {
if ( u2 - > ship = = sh ) {
2012-05-17 09:13:30 +02:00
leave_ship ( u2 ) ;
2010-08-08 10:06:34 +02:00
}
}
ADDMSG ( & u - > faction - > msgs , msg_message ( " shipdestroy " ,
2011-03-07 08:02:35 +01:00
" unit region ship " , u , r , sh ) ) ;
2010-08-08 10:06:34 +02:00
con = sh - > type - > construction ;
remove_ship ( & sh - > region - > ships , sh ) ;
} else {
/* partial destroy */
2011-03-07 08:02:35 +01:00
sh - > size - = ( sh - > type - > construction - > maxsize * n ) / 100 ;
2010-08-08 10:06:34 +02:00
ADDMSG ( & u - > faction - > msgs , msg_message ( " shipdestroy_partial " ,
2011-03-07 08:02:35 +01:00
" unit region ship " , u , r , sh ) ) ;
2010-08-08 10:06:34 +02:00
}
} else {
2012-05-17 01:52:01 +02:00
log_error ( " Die Einheit %s von %s war owner eines objects, war aber weder in einer Burg noch in einem Schiff. \n " , unitname ( u ) , u - > faction - > name , u - > faction - > email ) ;
2010-08-08 10:06:34 +02:00
}
if ( con ) {
/* TODO: Nicht an ZERST<53> RE mit Punktangabe angepa<70> t! */
int c ;
2011-03-07 08:02:35 +01:00
for ( c = 0 ; con - > materials [ c ] . number ; + + c ) {
const requirement * rq = con - > materials + c ;
int recycle = ( int ) ( rq - > recycle * rq - > number * size / con - > reqsize ) ;
2010-08-08 10:06:34 +02:00
if ( recycle ) {
change_resource ( u , rq - > rtype , recycle ) ;
}
}
}
return 0 ;
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
void build_road ( region * r , unit * u , int size , direction_t d )
2010-08-08 10:06:34 +02:00
{
int n , left ;
2011-03-07 08:02:35 +01:00
region * rn = rconnect ( r , d ) ;
2010-08-08 10:06:34 +02:00
assert ( u - > number ) ;
if ( ! eff_skill ( u , SK_ROAD_BUILDING , r ) ) {
cmistake ( u , u - > thisorder , 103 , MSG_PRODUCE ) ;
return ;
}
if ( besieged ( u ) ) {
cmistake ( u , u - > thisorder , 60 , MSG_PRODUCE ) ;
return ;
}
2011-03-07 08:02:35 +01:00
if ( rn = = NULL | | rn - > terrain - > max_road < 0 ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , u - > thisorder , 94 , MSG_PRODUCE ) ;
return ;
}
if ( r - > terrain - > max_road < 0 ) {
cmistake ( u , u - > thisorder , 94 , MSG_PRODUCE ) ;
return ;
}
if ( r - > terrain = = newterrain ( T_SWAMP ) ) {
/* wenn kein Damm existiert */
2011-03-07 08:02:35 +01:00
static const struct building_type * bt_dam ;
if ( ! bt_dam )
bt_dam = bt_find ( " dam " ) ;
2010-08-08 10:06:34 +02:00
assert ( bt_dam ) ;
if ( ! buildingtype_exists ( r , bt_dam , true ) ) {
cmistake ( u , u - > thisorder , 132 , MSG_PRODUCE ) ;
return ;
}
} else if ( r - > terrain = = newterrain ( T_DESERT ) ) {
2011-03-07 08:02:35 +01:00
static const struct building_type * bt_caravan ;
if ( ! bt_caravan )
bt_caravan = bt_find ( " caravan " ) ;
2010-08-08 10:06:34 +02:00
assert ( bt_caravan ) ;
/* wenn keine Karawanserei existiert */
if ( ! buildingtype_exists ( r , bt_caravan , true ) ) {
cmistake ( u , u - > thisorder , 133 , MSG_PRODUCE ) ;
return ;
}
} else if ( r - > terrain = = newterrain ( T_GLACIER ) ) {
2011-03-07 08:02:35 +01:00
static const struct building_type * bt_tunnel ;
if ( ! bt_tunnel )
bt_tunnel = bt_find ( " tunnel " ) ;
2010-08-08 10:06:34 +02:00
assert ( bt_tunnel ) ;
/* wenn kein Tunnel existiert */
if ( ! buildingtype_exists ( r , bt_tunnel , true ) ) {
cmistake ( u , u - > thisorder , 131 , MSG_PRODUCE ) ;
return ;
}
}
/* left kann man noch bauen */
left = r - > terrain - > max_road - rroad ( r , d ) ;
/* hoffentlich ist r->road <= r->terrain->max_road, n also >= 0 */
if ( left < = 0 ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , u - > thisorder ,
" error_roads_finished " , " " ) ) ;
2010-08-08 10:06:34 +02:00
return ;
}
2011-03-07 08:02:35 +01:00
if ( size > 0 )
left = MIN ( size , left ) ;
2010-08-08 10:06:34 +02:00
/* baumaximum anhand der rohstoffe */
2011-03-07 08:02:35 +01:00
if ( u - > race = = new_race [ RC_STONEGOLEM ] ) {
2010-08-08 10:06:34 +02:00
n = u - > number * GOLEM_STONE ;
} else {
n = get_pooled ( u , oldresourcetype [ R_STONE ] , GET_DEFAULT , left ) ;
2011-03-07 08:02:35 +01:00
if ( n = = 0 ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , u - > thisorder , 151 , MSG_PRODUCE ) ;
return ;
}
}
left = MIN ( n , left ) ;
/* n = maximum by skill. try to maximize it */
n = u - > number * eff_skill ( u , SK_ROAD_BUILDING , r ) ;
if ( n < left ) {
2011-03-07 08:02:35 +01:00
item * itm = * i_find ( & u - > items , olditemtype [ I_RING_OF_NIMBLEFINGER ] ) ;
if ( itm ! = NULL & & itm - > number > 0 ) {
2010-08-08 10:06:34 +02:00
int rings = MIN ( u - > number , itm - > number ) ;
2011-03-07 08:02:35 +01:00
n = n * ( ( roqf_factor ( ) - 1 ) * rings + u - > number ) / u - > number ;
2010-08-08 10:06:34 +02:00
}
}
if ( n < left ) {
int dm = get_effect ( u , oldpotiontype [ P_DOMORE ] ) ;
if ( dm ! = 0 ) {
int sk = eff_skill ( u , SK_ROAD_BUILDING , r ) ;
int todo = ( left - n + sk - 1 ) / sk ;
todo = MIN ( todo , u - > number ) ;
dm = MIN ( dm , todo ) ;
change_effect ( u , oldpotiontype [ P_DOMORE ] , - dm ) ;
n + = dm * sk ;
2011-03-07 08:02:35 +01:00
} /* Auswirkung Schaffenstrunk */
2010-08-08 10:06:34 +02:00
}
/* make minimum of possible and available: */
n = MIN ( left , n ) ;
/* n is now modified by several special effects, so we have to
* minimize it again to make sure the road will not grow beyond
* maximum . */
rsetroad ( r , d , rroad ( r , d ) + ( short ) n ) ;
if ( u - > race = = new_race [ RC_STONEGOLEM ] ) {
int golemsused = n / GOLEM_STONE ;
2011-03-07 08:02:35 +01:00
if ( n % GOLEM_STONE ! = 0 ) {
2010-08-08 10:06:34 +02:00
+ + golemsused ;
}
scale_number ( u , u - > number - golemsused ) ;
} else {
use_pooled ( u , oldresourcetype [ R_STONE ] , GET_DEFAULT , n ) ;
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
produceexp ( u , SK_ROAD_BUILDING , MIN ( n , u - > number ) ) ;
}
ADDMSG ( & u - > faction - > msgs , msg_message ( " buildroad " ,
2011-03-07 08:02:35 +01:00
" region unit size " , r , u , n ) ) ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
/* ** ** ** ** ** ** *
* new build rules *
* * * * * * * * * * * * * */
2011-03-07 08:02:35 +01:00
static int required ( int size , int msize , int maxneed )
2010-08-08 10:06:34 +02:00
/* um size von msize Punkten zu bauen,
* braucht man required von maxneed resourcen */
{
int used ;
used = size * maxneed / msize ;
if ( size * maxneed % msize )
+ + used ;
return used ;
}
static int
2011-03-07 08:02:35 +01:00
matmod ( const attrib * a , const unit * u , const resource_type * material ,
int value )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
for ( a = a_find ( ( attrib * ) a , & at_matmod ) ; a & & a - > type = = & at_matmod ;
a = a - > next ) {
mm_fun fun = ( mm_fun ) a - > data . f ;
2010-08-08 10:06:34 +02:00
value = fun ( u , material , value ) ;
2011-03-07 08:02:35 +01:00
if ( value < 0 )
return value ; /* pass errors to caller */
2010-08-08 10:06:34 +02:00
}
return value ;
}
int roqf_factor ( void )
{
int value = - 1 ;
2011-03-07 08:02:35 +01:00
if ( value < 0 ) {
2010-08-08 10:06:34 +02:00
value = get_param_int ( global . parameters , " rules.economy.roqf " , 10 ) ;
}
return value ;
}
/** Use up resources for building an object.
* Build up to ' size ' points of ' type ' , where ' completed '
* of the first object have already been finished . return the
* actual size that could be built .
*/
2011-03-07 08:02:35 +01:00
int build ( unit * u , const construction * ctype , int completed , int want )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction * type = ctype ;
int skills = INT_MAX ; /* number of skill points remainig */
2010-08-08 10:06:34 +02:00
int basesk = 0 ;
int made = 0 ;
2011-03-07 08:02:35 +01:00
if ( want < = 0 )
return 0 ;
if ( type = = NULL )
return 0 ;
if ( type - > improvement = = NULL & & completed = = type - > maxsize )
2010-08-08 10:06:34 +02:00
return ECOMPLETE ;
2011-03-07 08:02:35 +01:00
if ( type - > btype ! = NULL ) {
building * b ;
if ( ! u - > building | | u - > building - > type ! = type - > btype ) {
2010-08-08 10:06:34 +02:00
return EBUILDINGREQ ;
}
b = inside_building ( u ) ;
2011-03-07 08:02:35 +01:00
if ( b = = NULL )
return EBUILDINGREQ ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if ( type - > skill ! = NOSKILL ) {
2010-08-08 10:06:34 +02:00
int effsk ;
int dm = get_effect ( u , oldpotiontype [ P_DOMORE ] ) ;
assert ( u - > number ) ;
basesk = effskill ( u , type - > skill ) ;
2011-03-07 08:02:35 +01:00
if ( basesk = = 0 )
return ENEEDSKILL ;
2010-08-08 10:06:34 +02:00
effsk = basesk ;
if ( inside_building ( u ) ) {
effsk = skillmod ( u - > building - > type - > attribs , u , u - > region , type - > skill ,
2011-03-07 08:02:35 +01:00
effsk , SMF_PRODUCTION ) ;
2010-08-08 10:06:34 +02:00
}
effsk = skillmod ( type - > attribs , u , u - > region , type - > skill ,
2011-03-07 08:02:35 +01:00
effsk , SMF_PRODUCTION ) ;
if ( effsk < 0 )
return effsk ; /* pass errors to caller */
if ( effsk = = 0 )
return ENEEDSKILL ;
2010-08-08 10:06:34 +02:00
skills = effsk * u - > number ;
/* technically, nimblefinge and domore should be in a global set of
2011-03-07 08:02:35 +01:00
* " game " - attributes , ( as at_skillmod ) but for a while , we ' re leaving
* them in here . */
2010-08-08 10:06:34 +02:00
if ( dm ! = 0 ) {
/* Auswirkung Schaffenstrunk */
dm = MIN ( dm , u - > number ) ;
change_effect ( u , oldpotiontype [ P_DOMORE ] , - dm ) ;
skills + = dm * effsk ;
}
}
2011-03-07 08:02:35 +01:00
for ( ; want > 0 & & skills > 0 ; ) {
2010-08-08 10:06:34 +02:00
int c , n ;
/* skip over everything that's already been done:
* type - > improvement = = NULL means no more improvements , but no size limits
* type - > improvement = = type means build another object of the same time
* while material lasts type - > improvement = = x means build x when type
* is finished */
2011-03-07 08:02:35 +01:00
while ( type - > improvement ! = NULL & &
type - > improvement ! = type & &
type - > maxsize > 0 & & type - > maxsize < = completed ) {
2010-08-08 10:06:34 +02:00
completed - = type - > maxsize ;
type = type - > improvement ;
}
2011-03-07 08:02:35 +01:00
if ( type = = NULL ) {
if ( made = = 0 )
return ECOMPLETE ;
break ; /* completed */
2010-08-08 10:06:34 +02:00
}
/* Hier ist entweder maxsize == -1, oder completed < maxsize.
* Andernfalls ist das Datenfile oder sonstwas kaputt . . .
* ( enno ) : Nein , das ist f <EFBFBD> r Dinge , bei denen die n <EFBFBD> chste Ausbaustufe
* die gleiche wie die vorherige ist . z . b . gegenst <EFBFBD> nde .
*/
2011-03-07 08:02:35 +01:00
if ( type - > maxsize > 1 ) {
2010-08-08 10:06:34 +02:00
completed = completed % type - > maxsize ;
2011-03-07 08:02:35 +01:00
} else {
completed = 0 ;
assert ( type - > reqsize > = 1 ) ;
2010-08-08 10:06:34 +02:00
}
if ( basesk < type - > minskill ) {
2011-03-07 08:02:35 +01:00
if ( made = = 0 )
return ELOWSKILL ; /* not good enough to go on */
2010-08-08 10:06:34 +02:00
}
/* n = maximum buildable size */
if ( type - > minskill > 1 ) {
n = skills / type - > minskill ;
} else {
n = skills ;
}
/* Flinkfingerring wirkt nicht auf Mengenbegrenzte (magische)
* Talente */
2011-03-07 08:02:35 +01:00
if ( skill_limit ( u - > faction , type - > skill ) = = INT_MAX ) {
2010-08-08 10:06:34 +02:00
int i = 0 ;
2011-03-07 08:02:35 +01:00
item * itm = * i_find ( & u - > items , olditemtype [ I_RING_OF_NIMBLEFINGER ] ) ;
if ( itm ! = NULL )
i = itm - > number ;
if ( i > 0 ) {
2010-08-08 10:06:34 +02:00
int rings = MIN ( u - > number , i ) ;
2011-03-07 08:02:35 +01:00
n = n * ( ( roqf_factor ( ) - 1 ) * rings + u - > number ) / u - > number ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
if ( want > 0 ) {
2010-08-08 10:06:34 +02:00
n = MIN ( want , n ) ;
}
2011-03-07 08:02:35 +01:00
if ( type - > maxsize > 0 ) {
n = MIN ( type - > maxsize - completed , n ) ;
if ( type - > improvement = = NULL ) {
2010-08-08 10:06:34 +02:00
want = n ;
}
}
2011-03-07 08:02:35 +01:00
if ( type - > materials )
for ( c = 0 ; n > 0 & & type - > materials [ c ] . number ; c + + ) {
const struct resource_type * rtype = type - > materials [ c ] . rtype ;
int need , prebuilt ;
int canuse = get_pooled ( u , rtype , GET_DEFAULT , INT_MAX ) ;
if ( inside_building ( u ) ) {
canuse = matmod ( u - > building - > type - > attribs , u , rtype , canuse ) ;
}
if ( canuse < 0 )
return canuse ; /* pass errors to caller */
canuse = matmod ( type - > attribs , u , rtype , canuse ) ;
if ( type - > reqsize > 1 ) {
prebuilt =
required ( completed , type - > reqsize , type - > materials [ c ] . number ) ;
for ( ; n ; ) {
need =
required ( completed + n , type - > reqsize , type - > materials [ c ] . number ) ;
if ( need - prebuilt < = canuse )
break ;
- - n ; /* TODO: optimieren? */
}
} else {
int maxn = canuse / type - > materials [ c ] . number ;
if ( maxn < n )
n = maxn ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
if ( n < = 0 ) {
if ( made = = 0 )
return ENOMATERIALS ;
else
break ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if ( type - > materials )
for ( c = 0 ; type - > materials [ c ] . number ; c + + ) {
const struct resource_type * rtype = type - > materials [ c ] . rtype ;
int prebuilt =
required ( completed , type - > reqsize , type - > materials [ c ] . number ) ;
int need =
required ( completed + n , type - > reqsize , type - > materials [ c ] . number ) ;
int multi = 1 ;
int canuse = 100 ; /* normalization */
if ( inside_building ( u ) )
canuse = matmod ( u - > building - > type - > attribs , u , rtype , canuse ) ;
if ( canuse < 0 )
return canuse ; /* pass errors to caller */
canuse = matmod ( type - > attribs , u , rtype , canuse ) ;
assert ( canuse % 100 = = 0
| | ! " only constant multipliers are implemented in build() " ) ;
multi = canuse / 100 ;
if ( canuse < 0 )
return canuse ; /* pass errors to caller */
use_pooled ( u , rtype , GET_DEFAULT ,
( need - prebuilt + multi - 1 ) / multi ) ;
}
2010-08-08 10:06:34 +02:00
made + = n ;
skills - = n * type - > minskill ;
want - = n ;
completed = completed + n ;
}
/* Nur soviel PRODUCEEXP wie auch tatsaechlich gemacht wurde */
produceexp ( u , ctype - > skill , MIN ( made , u - > number ) ) ;
return made ;
}
2011-03-07 08:02:35 +01:00
message * msg_materials_required ( unit * u , order * ord ,
const construction * ctype , int multi )
2010-08-08 10:06:34 +02:00
{
int c ;
/* something missing from the list of materials */
2011-03-07 08:02:35 +01:00
resource * reslist = NULL ;
if ( multi < = 0 | | multi = = INT_MAX )
multi = 1 ;
for ( c = 0 ; ctype - > materials [ c ] . number ; + + c ) {
resource * res = malloc ( sizeof ( resource ) ) ;
2010-08-08 10:06:34 +02:00
res - > number = multi * ctype - > materials [ c ] . number / ctype - > reqsize ;
res - > type = ctype - > materials [ c ] . rtype ;
res - > next = reslist ;
reslist = res ;
}
return msg_feedback ( u , ord , " build_required " , " required " , reslist ) ;
}
2011-03-07 08:02:35 +01:00
int maxbuild ( const unit * u , const construction * cons )
2010-08-08 10:06:34 +02:00
/* calculate maximum size that can be built from available material */
2011-03-07 08:02:35 +01:00
/* !! ignores maximum objectsize and improvements... */
2010-08-08 10:06:34 +02:00
{
int c ;
int maximum = INT_MAX ;
2011-03-07 08:02:35 +01:00
for ( c = 0 ; cons - > materials [ c ] . number ; c + + ) {
const resource_type * rtype = cons - > materials [ c ] . rtype ;
2010-08-08 10:06:34 +02:00
int have = get_pooled ( u , rtype , GET_DEFAULT , INT_MAX ) ;
int need = required ( 1 , cons - > reqsize , cons - > materials [ c ] . number ) ;
2011-03-07 08:02:35 +01:00
if ( have < need ) {
2010-08-08 10:06:34 +02:00
return 0 ;
2011-03-07 08:02:35 +01:00
} else
maximum = MIN ( maximum , have / need ) ;
2010-08-08 10:06:34 +02:00
}
return maximum ;
}
/** old build routines */
void
build_building ( unit * u , const building_type * btype , int want , order * ord )
{
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
2010-08-08 10:06:34 +02:00
boolean newbuilding = false ;
int n = want , built = 0 , id ;
2011-03-07 08:02:35 +01:00
building * b = NULL ;
2010-08-08 10:06:34 +02:00
/* einmalige Korrektur */
2011-03-07 08:02:35 +01:00
const char * btname ;
order * new_order = NULL ;
const struct locale * lang = u - > faction - > locale ;
2010-08-08 10:06:34 +02:00
static int rule_other = - 1 ;
assert ( u - > number ) ;
if ( eff_skill ( u , SK_BUILDING , r ) = = 0 ) {
cmistake ( u , ord , 101 , MSG_PRODUCE ) ;
return ;
}
/* Falls eine Nummer angegeben worden ist, und ein Gebaeude mit der
* betreffenden Nummer existiert , ist b nun gueltig . Wenn keine Burg
* gefunden wurde , dann wird nicht einfach eine neue erbaut . Ansonsten
* baut man an der eigenen burg weiter . */
/* Wenn die angegebene Nummer falsch ist, KEINE Burg bauen! */
id = getid ( ) ;
2011-03-07 08:02:35 +01:00
if ( id > 0 ) { /* eine Nummer angegeben, keine neue Burg bauen */
2010-08-08 10:06:34 +02:00
b = findbuilding ( id ) ;
if ( ! b | | b - > region ! = u - > region ) { /* eine Burg mit dieser Nummer gibt es hier nicht */
/* vieleicht Tippfehler und die eigene Burg ist gemeint? */
2011-03-07 08:02:35 +01:00
if ( u - > building & & u - > building - > type = = btype ) {
2010-08-08 10:06:34 +02:00
b = u - > building ;
} else {
/* keine neue Burg anfangen wenn eine Nummer angegeben war */
cmistake ( u , ord , 6 , MSG_PRODUCE ) ;
return ;
}
}
2011-03-07 08:02:35 +01:00
} else if ( u - > building & & u - > building - > type = = btype ) {
2010-08-08 10:06:34 +02:00
b = u - > building ;
}
2011-03-07 08:02:35 +01:00
if ( b )
btype = b - > type ;
2010-08-08 10:06:34 +02:00
2011-03-09 00:25:51 +01:00
if ( fval ( btype , BTF_UNIQUE ) & & buildingtype_exists ( r , btype , false ) ) {
2010-08-08 10:06:34 +02:00
/* only one of these per region */
cmistake ( u , ord , 93 , MSG_PRODUCE ) ;
return ;
}
if ( besieged ( u ) ) {
/* units under siege can not build */
cmistake ( u , ord , 60 , MSG_PRODUCE ) ;
return ;
}
if ( btype - > flags & BTF_NOBUILD ) {
/* special building, cannot be built */
cmistake ( u , ord , 221 , MSG_PRODUCE ) ;
return ;
}
2011-03-07 08:02:35 +01:00
if ( r - > terrain - > max_road < = 0 ) {
2010-09-12 04:30:00 +02:00
/* special terrain, cannot build */
cmistake ( u , ord , 221 , MSG_PRODUCE ) ;
return ;
}
2010-08-08 10:06:34 +02:00
if ( btype - > flags & BTF_ONEPERTURN ) {
2011-03-07 08:02:35 +01:00
if ( b & & fval ( b , BLD_EXPANDED ) ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 318 , MSG_PRODUCE ) ;
return ;
}
n = 1 ;
}
if ( b ) {
2011-03-07 08:02:35 +01:00
if ( rule_other < 0 ) {
rule_other =
get_param_int ( global . parameters , " rules.build.other_buildings " , 1 ) ;
2010-08-08 10:06:34 +02:00
}
if ( ! rule_other ) {
2011-03-07 08:02:35 +01:00
unit * owner = building_owner ( b ) ;
if ( ! owner | | owner - > faction ! = u - > faction ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 1222 , MSG_PRODUCE ) ;
return ;
}
}
}
2011-03-07 08:02:35 +01:00
if ( b )
built = b - > size ;
if ( n < = 0 | | n = = INT_MAX ) {
if ( b = = NULL ) {
if ( btype - > maxsize > 0 ) {
2010-08-08 10:06:34 +02:00
n = btype - > maxsize - built ;
} else {
n = INT_MAX ;
}
} else {
if ( b - > type - > maxsize > 0 ) {
n = b - > type - > maxsize - built ;
} else {
n = INT_MAX ;
}
}
}
built = build ( u , btype - > construction , built , n ) ;
switch ( built ) {
2011-03-08 08:44:20 +01:00
case ECOMPLETE :
/* the building is already complete */
cmistake ( u , ord , 4 , MSG_PRODUCE ) ;
return ;
case ENOMATERIALS :
ADDMSG ( & u - > faction - > msgs , msg_materials_required ( u , ord ,
btype - > construction , want ) ) ;
return ;
case ELOWSKILL :
case ENEEDSKILL :
/* no skill, or not enough skill points to build */
cmistake ( u , ord , 50 , MSG_PRODUCE ) ;
return ;
2010-08-08 10:06:34 +02:00
}
/* at this point, the building size is increased. */
2011-03-07 08:02:35 +01:00
if ( b = = NULL ) {
2010-08-08 10:06:34 +02:00
/* build a new building */
b = new_building ( btype , r , lang ) ;
b - > type = btype ;
fset ( b , BLD_MAINTAINED ) ;
/* Die Einheit befindet sich automatisch im Inneren der neuen Burg. */
if ( leave ( u , false ) ) {
2012-05-18 00:23:15 +02:00
u_set_building ( u , b ) ;
assert ( building_owner ( b ) = = u ) ;
2010-08-08 10:06:34 +02:00
}
# ifdef WDW_PYRAMID
2011-03-07 08:02:35 +01:00
if ( b - > type = = bt_find ( " pyramid " ) & & f_get_alliance ( u - > faction ) ! = NULL ) {
attrib * a = a_add ( & b - > attribs , a_new ( & at_alliance ) ) ;
2010-08-08 10:06:34 +02:00
a - > data . i = u - > faction - > alliance - > id ;
}
# endif
newbuilding = true ;
}
btname = LOC ( lang , btype - > _name ) ;
2011-03-07 08:02:35 +01:00
if ( want - built < = 0 ) {
2010-08-08 10:06:34 +02:00
/* geb<65> ude fertig */
new_order = default_order ( lang ) ;
2011-03-07 08:02:35 +01:00
} else if ( want ! = INT_MAX ) {
2010-08-08 10:06:34 +02:00
/* reduzierte restgr<67> <72> e */
2011-03-07 08:02:35 +01:00
const char * hasspace = strchr ( btname , ' ' ) ;
2010-08-08 10:06:34 +02:00
if ( hasspace ) {
2011-03-07 08:02:35 +01:00
new_order =
create_order ( K_MAKE , lang , " %d \" %s \" %i " , n - built , btname , b - > no ) ;
2010-08-08 10:06:34 +02:00
} else {
2011-03-07 08:02:35 +01:00
new_order =
create_order ( K_MAKE , lang , " %d %s %i " , n - built , btname , b - > no ) ;
2010-08-08 10:06:34 +02:00
}
} else if ( btname ) {
/* Neues Haus, Befehl mit Geb<65> udename */
2011-03-07 08:02:35 +01:00
const char * hasspace = strchr ( btname , ' ' ) ;
2010-08-08 10:06:34 +02:00
if ( hasspace ) {
new_order = create_order ( K_MAKE , lang , " \" %s \" %i " , btname , b - > no ) ;
} else {
new_order = create_order ( K_MAKE , lang , " %s %i " , btname , b - > no ) ;
}
}
if ( new_order ) {
replace_order ( & u - > orders , ord , new_order ) ;
free_order ( new_order ) ;
}
b - > size + = built ;
fset ( b , BLD_EXPANDED ) ;
update_lighthouse ( b ) ;
ADDMSG ( & u - > faction - > msgs , msg_message ( " buildbuilding " ,
2011-03-07 08:02:35 +01:00
" building unit size " , b , u , built ) ) ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
static void build_ship ( unit * u , ship * sh , int want )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction * construction = sh - > type - > construction ;
2010-08-08 10:06:34 +02:00
int size = ( sh - > size * DAMAGE_SCALE - sh - > damage ) / DAMAGE_SCALE ;
int n ;
int can = build ( u , construction , size , want ) ;
2011-03-07 08:02:35 +01:00
if ( ( n = construction - > maxsize - sh - > size ) > 0 & & can > 0 ) {
if ( can > = n ) {
sh - > size + = n ;
can - = n ;
} else {
sh - > size + = can ;
n = can ;
can = 0 ;
2010-08-08 10:06:34 +02:00
}
}
if ( sh - > damage & & can ) {
int repair = MIN ( sh - > damage , can * DAMAGE_SCALE ) ;
n + = repair / DAMAGE_SCALE ;
2011-03-07 08:02:35 +01:00
if ( repair % DAMAGE_SCALE )
+ + n ;
2010-08-08 10:06:34 +02:00
sh - > damage = sh - > damage - repair ;
}
2011-03-07 08:02:35 +01:00
if ( n )
ADDMSG ( & u - > faction - > msgs ,
msg_message ( " buildship " , " ship unit size " , sh , u , n ) ) ;
2010-08-08 10:06:34 +02:00
}
void
2011-03-07 08:02:35 +01:00
create_ship ( region * r , unit * u , const struct ship_type * newtype , int want ,
order * ord )
2010-08-08 10:06:34 +02:00
{
ship * sh ;
int msize ;
2011-03-07 08:02:35 +01:00
const construction * cons = newtype - > construction ;
order * new_order ;
2010-08-08 10:06:34 +02:00
if ( ! eff_skill ( u , SK_SHIPBUILDING , r ) ) {
cmistake ( u , ord , 100 , MSG_PRODUCE ) ;
return ;
}
if ( besieged ( u ) ) {
cmistake ( u , ord , 60 , MSG_PRODUCE ) ;
return ;
}
/* check if skill and material for 1 size is available */
if ( eff_skill ( u , cons - > skill , r ) < cons - > minskill ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , u - > thisorder ,
" error_build_skill_low " , " value name " , cons - > minskill ,
newtype - > name [ 1 ] ) ) ;
2010-08-08 10:06:34 +02:00
return ;
}
msize = maxbuild ( u , cons ) ;
2011-03-07 08:02:35 +01:00
if ( msize = = 0 ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 88 , MSG_PRODUCE ) ;
return ;
}
2011-03-07 08:02:35 +01:00
if ( want > 0 )
want = MIN ( want , msize ) ;
else
want = msize ;
2010-08-08 10:06:34 +02:00
2012-05-17 09:13:30 +02:00
sh = new_ship ( newtype , r , u - > faction - > locale ) ;
2010-08-08 10:06:34 +02:00
if ( leave ( u , false ) ) {
if ( fval ( u - > race , RCF_CANSAIL ) ) {
2012-05-17 09:13:30 +02:00
u_set_ship ( u , sh ) ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-07 08:02:35 +01:00
new_order =
create_order ( K_MAKE , u - > faction - > locale , " %s %i " , LOC ( u - > faction - > locale ,
parameters [ P_SHIP ] ) , sh - > no ) ;
2010-08-08 10:06:34 +02:00
replace_order ( & u - > orders , ord , new_order ) ;
free_order ( new_order ) ;
build_ship ( u , sh , want ) ;
}
2011-03-07 08:02:35 +01:00
void continue_ship ( region * r , unit * u , int want )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
const construction * cons ;
2010-08-08 10:06:34 +02:00
ship * sh ;
int msize ;
if ( ! eff_skill ( u , SK_SHIPBUILDING , r ) ) {
cmistake ( u , u - > thisorder , 100 , MSG_PRODUCE ) ;
return ;
}
/* Die Schiffsnummer bzw der Schiffstyp wird eingelesen */
sh = getship ( r ) ;
2011-03-07 08:02:35 +01:00
if ( ! sh )
sh = u - > ship ;
2010-08-08 10:06:34 +02:00
if ( ! sh ) {
cmistake ( u , u - > thisorder , 20 , MSG_PRODUCE ) ;
return ;
}
cons = sh - > type - > construction ;
2011-03-07 08:02:35 +01:00
assert ( cons - > improvement = = NULL ) ; /* sonst ist construction::size nicht ship_type::maxsize */
if ( sh - > size = = cons - > maxsize & & ! sh - > damage ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , u - > thisorder , 16 , MSG_PRODUCE ) ;
return ;
}
if ( eff_skill ( u , cons - > skill , r ) < cons - > minskill ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , u - > thisorder ,
" error_build_skill_low " , " value name " , cons - > minskill ,
sh - > type - > name [ 1 ] ) ) ;
2010-08-08 10:06:34 +02:00
return ;
}
msize = maxbuild ( u , cons ) ;
2011-03-07 08:02:35 +01:00
if ( msize = = 0 ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , u - > thisorder , 88 , MSG_PRODUCE ) ;
return ;
}
2011-03-07 08:02:35 +01:00
if ( want > 0 )
want = MIN ( want , msize ) ;
else
want = msize ;
2010-08-08 10:06:34 +02:00
build_ship ( u , sh , want ) ;
}
2011-03-07 08:02:35 +01:00
2010-08-08 10:06:34 +02:00
/* ------------------------------------------------------------- */
2011-03-07 08:02:35 +01:00
static boolean mayenter ( region * r , unit * u , building * b )
2010-08-08 10:06:34 +02:00
{
unit * u2 ;
2011-03-07 08:02:35 +01:00
if ( fval ( b , BLD_UNGUARDED ) )
return true ;
2010-08-08 10:06:34 +02:00
u2 = building_owner ( b ) ;
2011-03-07 08:02:35 +01:00
if ( u2 = = NULL | | ucontact ( u2 , u )
| | alliedunit ( u2 , u - > faction , HELP_GUARD ) )
return true ;
2010-08-08 10:06:34 +02:00
return false ;
}
2012-05-17 10:14:43 +02:00
static int mayboard ( const unit * u , ship * sh )
2010-08-08 10:06:34 +02:00
{
2012-05-17 21:23:25 +02:00
unit * u2 = ship_owner ( sh ) ;
2010-08-08 10:06:34 +02:00
return ( ! u2 | | ucontact ( u2 , u ) | | alliedunit ( u2 , u - > faction , HELP_GUARD ) ) ;
}
2011-03-07 08:02:35 +01:00
int leave_cmd ( unit * u , struct order * ord )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
2010-08-08 10:06:34 +02:00
if ( fval ( u , UFL_ENTER ) ) {
/* if we just entered this round, then we don't leave again */
return 0 ;
}
if ( fval ( r - > terrain , SEA_REGION ) & & u - > ship ) {
2011-03-07 08:02:35 +01:00
if ( ! fval ( u - > race , RCF_SWIM ) ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 11 , MSG_MOVE ) ;
return 0 ;
}
if ( has_horses ( u ) ) {
cmistake ( u , ord , 231 , MSG_MOVE ) ;
return 0 ;
}
}
if ( ! slipthru ( r , u , u - > building ) ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , u - > thisorder , " entrance_besieged " ,
" building " , u - > building ) ) ;
2010-08-08 10:06:34 +02:00
} else {
leave ( u , true ) ;
}
return 0 ;
}
2011-03-07 08:02:35 +01:00
static boolean enter_ship ( unit * u , struct order * ord , int id , boolean report )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
ship * sh ;
2010-08-08 10:06:34 +02:00
/* Mu<4D> abgefangen werden, sonst k<> nnten Schwimmer an
* Bord von Schiffen an Land gelangen . */
2011-03-07 08:02:35 +01:00
if ( ! fval ( u - > race , RCF_CANSAIL ) | | ( ! fval ( u - > race , RCF_WALK )
& & ! fval ( u - > race , RCF_FLY ) ) ) {
2010-08-08 10:06:34 +02:00
cmistake ( u , ord , 233 , MSG_MOVE ) ;
return false ;
}
sh = findship ( id ) ;
2011-03-07 08:02:35 +01:00
if ( sh = = NULL | | sh - > region ! = r ) {
if ( report )
cmistake ( u , ord , 20 , MSG_MOVE ) ;
2010-08-08 10:06:34 +02:00
return false ;
}
2011-03-07 08:02:35 +01:00
if ( sh = = u - > ship )
return true ;
2010-08-08 10:06:34 +02:00
if ( ! mayboard ( u , sh ) ) {
2011-03-07 08:02:35 +01:00
if ( report )
cmistake ( u , ord , 34 , MSG_MOVE ) ;
2010-08-08 10:06:34 +02:00
return false ;
}
if ( CheckOverload ( ) ) {
int sweight , scabins ;
int mweight = shipcapacity ( sh ) ;
int mcabins = sh - > type - > cabins ;
2011-03-07 08:02:35 +01:00
if ( mweight > 0 ) {
2010-08-08 10:06:34 +02:00
getshipweight ( sh , & sweight , & scabins ) ;
sweight + = weight ( u ) ;
if ( mcabins ) {
int pweight = u - > number * u - > race - > weight ;
/* weight goes into number of cabins, not cargo */
scabins + = pweight ;
sweight - = pweight ;
}
if ( sweight > mweight | | ( mcabins & & ( scabins > mcabins ) ) ) {
2011-03-07 08:02:35 +01:00
if ( report )
cmistake ( u , ord , 34 , MSG_MOVE ) ;
2010-08-08 10:06:34 +02:00
return false ;
}
}
}
if ( leave ( u , false ) ) {
2012-05-17 09:13:30 +02:00
u_set_ship ( u , sh ) ;
2010-08-08 10:06:34 +02:00
fset ( u , UFL_ENTER ) ;
}
return true ;
}
2011-03-07 08:02:35 +01:00
static boolean enter_building ( unit * u , order * ord , int id , boolean report )
2010-08-08 10:06:34 +02:00
{
2011-03-07 08:02:35 +01:00
region * r = u - > region ;
building * b ;
2010-08-08 10:06:34 +02:00
/* Schwimmer k<> nnen keine Geb<65> ude betreten, au<61> er diese sind
2011-03-07 08:02:35 +01:00
* auf dem Ozean */
2010-08-08 10:06:34 +02:00
if ( ! fval ( u - > race , RCF_WALK ) & & ! fval ( u - > race , RCF_FLY ) ) {
if ( ! fval ( r - > terrain , SEA_REGION ) ) {
if ( report ) {
cmistake ( u , ord , 232 , MSG_MOVE ) ;
}
return false ;
}
}
b = findbuilding ( id ) ;
2011-03-07 08:02:35 +01:00
if ( b = = NULL | | b - > region ! = r ) {
2010-08-08 10:06:34 +02:00
if ( report ) {
cmistake ( u , ord , 6 , MSG_MOVE ) ;
}
return false ;
}
if ( ! mayenter ( r , u , b ) ) {
if ( report ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , ord , " entrance_denied " ,
" building " , b ) ) ;
2010-08-08 10:06:34 +02:00
}
return false ;
}
if ( ! slipthru ( r , u , b ) ) {
if ( report ) {
2011-03-07 08:02:35 +01:00
ADDMSG ( & u - > faction - > msgs , msg_feedback ( u , ord , " entrance_besieged " ,
" building " , b ) ) ;
2010-08-08 10:06:34 +02:00
}
return false ;
}
if ( leave ( u , false ) ) {
fset ( u , UFL_ENTER ) ;
2012-05-18 00:23:15 +02:00
u_set_building ( u , b ) ;
2010-08-08 10:06:34 +02:00
return true ;
}
return false ;
}
2012-05-17 09:13:30 +02:00
void do_misc ( region * r , int is_final_attempt )
2010-08-08 10:06:34 +02:00
{
unit * * uptr , * uc ;
for ( uc = r - > units ; uc ; uc = uc - > next ) {
2011-03-07 08:02:35 +01:00
order * ord ;
2010-08-08 10:06:34 +02:00
for ( ord = uc - > orders ; ord ; ord = ord - > next ) {
2011-03-08 08:44:20 +01:00
keyword_t kwd = get_keyword ( ord ) ;
if ( kwd = = K_CONTACT ) {
2012-05-17 09:13:30 +02:00
contact_cmd ( uc , ord , is_final_attempt ) ;
2010-08-08 10:06:34 +02:00
}
}
}
for ( uptr = & r - > units ; * uptr ; ) {
2011-03-07 08:02:35 +01:00
unit * u = * uptr ;
order * * ordp = & u - > orders ;
2010-08-08 10:06:34 +02:00
while ( * ordp ) {
2011-03-07 08:02:35 +01:00
order * ord = * ordp ;
2010-08-08 10:06:34 +02:00
if ( get_keyword ( ord ) = = K_ENTER ) {
param_t p ;
int id ;
2011-03-07 08:02:35 +01:00
unit * ulast = NULL ;
2012-05-16 21:06:44 +02:00
const char * s ;
2010-08-08 10:06:34 +02:00
init_tokens ( ord ) ;
skip_token ( ) ;
2012-05-16 21:06:44 +02:00
s = getstrtoken ( ) ;
p = findparam_ex ( s , u - > faction - > locale ) ;
2010-08-08 10:06:34 +02:00
id = getid ( ) ;
switch ( p ) {
2011-03-08 08:44:20 +01:00
case P_BUILDING :
case P_GEBAEUDE :
if ( u - > building & & u - > building - > no = = id )
break ;
2012-05-17 09:13:30 +02:00
if ( enter_building ( u , ord , id , is_final_attempt ) ) {
2011-03-08 08:44:20 +01:00
unit * ub ;
for ( ub = u ; ub ; ub = ub - > next ) {
if ( ub - > building = = u - > building ) {
ulast = ub ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-08 08:44:20 +01:00
}
break ;
2011-03-07 08:02:35 +01:00
2011-03-08 08:44:20 +01:00
case P_SHIP :
if ( u - > ship & & u - > ship - > no = = id )
break ;
2012-05-17 09:13:30 +02:00
if ( enter_ship ( u , ord , id , is_final_attempt ) ) {
2011-03-08 08:44:20 +01:00
unit * ub ;
ulast = u ;
for ( ub = u ; ub ; ub = ub - > next ) {
if ( ub - > ship = = u - > ship ) {
ulast = ub ;
2010-08-08 10:06:34 +02:00
}
}
2011-03-08 08:44:20 +01:00
}
break ;
2010-08-08 10:06:34 +02:00
2011-03-08 08:44:20 +01:00
default :
2012-05-17 09:13:30 +02:00
if ( is_final_attempt ) {
2011-03-08 08:44:20 +01:00
cmistake ( u , ord , 79 , MSG_MOVE ) ;
2012-05-17 09:13:30 +02:00
}
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if ( ulast ! = NULL ) {
2010-08-08 10:06:34 +02:00
/* Wenn wir hier angekommen sind, war der Befehl
2011-03-07 08:02:35 +01:00
* erfolgreich und wir l <EFBFBD> schen ihn , damit er im
* zweiten Versuch nicht nochmal ausgef <EFBFBD> hrt wird . */
2010-08-08 10:06:34 +02:00
* ordp = ord - > next ;
ord - > next = NULL ;
free_order ( ord ) ;
2011-03-07 08:02:35 +01:00
if ( ulast ! = u ) {
2010-08-08 10:06:34 +02:00
/* put u behind ulast so it's the last unit in the building */
* uptr = u - > next ;
u - > next = ulast - > next ;
ulast - > next = u ;
}
break ;
}
}
2011-03-07 08:02:35 +01:00
if ( * ordp = = ord )
ordp = & ord - > next ;
2010-08-08 10:06:34 +02:00
}
2011-03-07 08:02:35 +01:00
if ( * uptr = = u )
uptr = & u - > next ;
2010-08-08 10:06:34 +02:00
}
}