diff --git a/doc/ChangeLog.html b/doc/ChangeLog.html index ac778d02c..0d9d502c7 100644 --- a/doc/ChangeLog.html +++ b/doc/ChangeLog.html @@ -36,6 +36,7 @@ <li>Future 1.19 Release<p> <ul> <li>++ + <li>New resource harvesting, no hardcoded unit types (from Crestez Leonard). <li>NEW_UI: Fixed save/load (from Martin Renold). <li>Pathfinder fixes for when the goal moves (EXPERIMENTAL) (from Russell Smith). <li>Added repair-costs and repair-hp for units that can be repaired (from Russell Smith). diff --git a/src/action/Module.make b/src/action/Module.make index d5012f4a5..5f355643b 100644 --- a/src/action/Module.make +++ b/src/action/Module.make @@ -27,11 +27,11 @@ MODULE = src/action MSRC = action_attack.c action_board.c action_build.c action_demolish.c \ - action_die.c action_follow.c action_harvest.c \ - action_move.c action_patrol.c action_repair.c action_research.c \ - action_resource.c action_returngoods.c action_spellcast.c \ - action_stand.c action_still.c action_train.c action_unload.c \ - action_upgradeto.c actions.c command.c + action_die.c action_follow.c action_move.c action_patrol.c \ + action_repair.c action_research.c action_resource.c \ + action_returngoods.c action_spellcast.c action_stand.c \ + action_still.c action_train.c action_unload.c action_upgradeto.c \ + actions.c command.c SRC+= $(addprefix $(MODULE)/,$(MSRC)) HDRS+= diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp index 38113a684..3e60f9f0c 100644 --- a/src/action/action_build.cpp +++ b/src/action/action_build.cpp @@ -392,7 +392,7 @@ global void HandleActionBuilded(Unit* unit) // // If we can harvest from the new building, do it. // - if (worker->Type->Harvester&&worker->Type->ResourceHarvested==type->GivesResource) { + if (worker->Type->ResInfo[type->GivesResource]) { CommandResource(worker,unit,0); } } diff --git a/src/action/action_harvest.cpp b/src/action/action_harvest.cpp deleted file mode 100644 index 4b1038217..000000000 --- a/src/action/action_harvest.cpp +++ /dev/null @@ -1,484 +0,0 @@ -// _________ __ __ -// / _____// |_____________ _/ |______ ____ __ __ ______ -// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ -// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | -// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > -// \/ \/ \//_____/ \/ -// ______________________ ______________________ -// T H E W A R B E G I N S -// Stratagus - A free fantasy real time strategy game engine -// -/**@name action_harvest.c - The harvest action. */ -// -// (c) Copyright 1998-2001,2003 by Lutz Sammer -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 2 dated June, 1991. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -// -// $Id$ - -//@{ - -/*---------------------------------------------------------------------------- --- Includes -----------------------------------------------------------------------------*/ - -#include <stdio.h> -#include <stdlib.h> - -#include "stratagus.h" -#include "unittype.h" -#include "player.h" -#include "unit.h" -#include "actions.h" -#include "sound.h" -#include "tileset.h" -#include "map.h" -#include "interface.h" -#include "pathfinder.h" -#include "upgrade_structs.h" - -// FIXME: Should combine all the resource functions -// FIXME: Should update buttons if the action changes? - -/*---------------------------------------------------------------------------- --- Functions -----------------------------------------------------------------------------*/ - -/** -** Move to forest. -** -** @param unit Pointer to worker unit. -** -** @return TRUE if reached, otherwise FALSE. -*/ -local int MoveToWood(Unit* unit) -{ - int i; - int x; - int y; - - if( (i=DoActionMove(unit))>=0 ) { // reached end-point? - return 0; - } - - if( i==PF_UNREACHABLE ) { -newtry: - if( FindWoodInSight(unit,&unit->Orders[0].X,&unit->Orders[0].Y) ) { - - // Move to new wood position - unit->Orders[0].Goal=NoUnitP; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=1; - NewResetPath(unit); - return 0; - } - - // FIXME: Should do more tries - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } - - x=unit->Orders[0].X; - y=unit->Orders[0].Y; - - if( !CheckedForestOnMap(x,y) ) { - // - // Fast check surrounding for forest - // - x=unit->X; - y=unit->Y; - if( CheckedForestOnMap(x-1,y-1) ) { - --x; - --y; - } else if( CheckedForestOnMap(x-1,y+0) ) { - --x; - } else if( CheckedForestOnMap(x-1,y+1) ) { - --x; - ++y; - } else if( CheckedForestOnMap(x+0,y-1) ) { - --y; - } else if( CheckedForestOnMap(x+0,y+1) ) { - ++y; - } else if( CheckedForestOnMap(x+1,y-1) ) { - ++x; - --y; - } else if( CheckedForestOnMap(x+1,y+0) ) { - ++x; - } else if( CheckedForestOnMap(x+1,y+1) ) { - ++x; - ++y; - } else { - DebugLevel3Fn("No Wood, Trying a better spot\n"); - goto newtry; - } - } - - // FIXME: don't chop the same wood! - - unit->Orders[0].X=x; - unit->Orders[0].Y=y; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=0; - DebugCheck( unit->Orders[0].Action!=UnitActionHarvest ); - // turn to wood - UnitHeadingFromDeltaXY(unit,x-unit->X,y-unit->Y); - //unit->Value=unit->Data.Harvest.WoodToHarvest; - - DebugCheck( unit->Wait!=1 ); - - return 1; -} - -/** -** Chop the wood. -** -** @param unit Pointer to worker unit. -** -** @return TRUE if ready, otherwise FALSE. -*/ -local int ChopWood(Unit* unit) -{ - Unit* destu; - int flags; - - flags=UnitShowAnimation(unit,unit->Type->Animations->Attack); - - if( (flags&AnimationSound) && (UnitVisibleOnMap(unit) || ReplayRevealMap) ) { - PlayUnitSound(unit,VoiceTreeChopping); - } - - if( unit->Reset ) { - - DebugCheck( unit->Wait!=1 ); - - // - // This a work around the bug: "lumber bug" - // We give a worker a new command and in the next cycle - // the worker is ready chopping. - // -#if 1 - if( unit->Orders[1].Action==UnitActionHarvest - || unit->Orders[1].Action==UnitActionResource ) { - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } -#endif - - // - // Wood gone while chopping? - // - if( !ForestOnMap(unit->Orders[0].X,unit->Orders[0].Y) ) { - if( FindWoodInSight(unit,&unit->Orders[0].X,&unit->Orders[0].Y) ) { - unit->Orders[0].Goal=NoUnitP; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=0; - DebugCheck( unit->Orders[0].Action!=UnitActionHarvest ); - unit->Orders[0].X--; - unit->Orders[0].Y--; - NewResetPath(unit); - unit->SubAction=1; - } else { - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - DebugLevel3Fn("NO-WOOD in sight range\n"); - } - return 0; - } - - // - // Ready chopping wood? - // - if( !--unit->Value ) { - // Have wood - unit->Player->UnitTypesCount[unit->Type->Type]--; - if( unit->Type==UnitTypeOrcWorker ) { - unit->Type=UnitTypeOrcWorkerWithWood; - } else if( unit->Type==UnitTypeHumanWorker ) { - unit->Type=UnitTypeHumanWorkerWithWood; - } else { - // FIXME: support more races! - DebugLevel0Fn("Wrong unit for chopping wood %d\n" - _C_ unit->Type->Type); - } - unit->Player->UnitTypesCount[unit->Type->Type]++; - - // - // Update the display. - // - CheckUnitToBeDrawn(unit); - if( IsOnlySelected(unit) ) { // update display -#ifndef NEW_UI - UpdateButtonPanel(); -#else - SelectedUnitChanged(); -#endif - } - - // - // Update the map. - // - MapRemoveWood(unit->Orders[0].X,unit->Orders[0].Y); -#if 0 - /* - * This call turned out not to be sufficient because MapRemoveWood() - * may actually remove more that one wood tile (x,y) due to - * "wood fixing" code, see action_harvest.c . So the pathfinder - * callback had to moved directly into MapRemoveWood(). - */ -#ifdef HIERARCHIC_PATHFINDER - PfHierMapChangedCallback (unit->Orders[0].X, unit->Orders[0].Y, - unit->Orders[0].X, unit->Orders[0].Y); -#endif -#endif - - // - // Find place to return wood. - // - // NOTE: unit->Orders[0].X && unit->Orders[0].Y holds return place. - unit->Orders[0].X=unit->X; - unit->Orders[0].Y=unit->Y; - if( !(destu=FindDeposit(unit->Player,unit->X,unit->Y,WoodCost)) ) { - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - } else { - unit->Orders[0].RangeX=unit->Orders[0].RangeY=0; - unit->Orders[0].Goal=destu; - RefsDebugCheck( !destu->Refs ); - destu->Refs++; - DebugCheck( unit->Orders[0].Action!=UnitActionHarvest ); - NewResetPath(unit); - return 1; - } - } - } - return 0; -} - -/** -** Return with the wood. -** -** @param unit unit pointer. -** -** @return TRUE if reached, otherwise FALSE. -*/ -local int ReturnWithWood(Unit* unit) -{ - Unit* destu; - int i; - - destu=unit->Orders[0].Goal; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=1; - DebugCheck( !destu ); - - i=DoActionMove(unit); - if( i>=0 && (!unit->Reset || !(destu->Destroyed || destu->Removed - || !destu->HP || destu->Orders[0].Action==UnitActionDie)) ) { - return 0; - } - - if( !destu ) { - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } - - // - // Target is dead, stop harvest - // - if( destu->Destroyed ) { - DebugLevel0Fn("Destroyed unit\n"); - RefsDebugCheck( !destu->Refs ); - if( !--destu->Refs ) { - ReleaseUnit(destu); - } - unit->Orders[0].Goal=NoUnitP; - // FIXME: perhaps we should choose an alternative - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } else if( destu->Removed || !destu->HP - || destu->Orders[0].Action==UnitActionDie ) { - RefsDebugCheck( !destu->Refs ); - --destu->Refs; - RefsDebugCheck( !destu->Refs ); - unit->Orders[0].Goal=NoUnitP; - // FIXME: perhaps we should choose an alternative - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } - - DebugCheck( unit->Wait!=1 ); - DebugCheck( unit->Orders[0].Action!=UnitActionHarvest ); - - // - // If depot is still under construction, wait! - // - if( destu->Orders[0].Action==UnitActionBuilded ) { - DebugLevel2Fn("Invalid depot\n"); - return 0; - } - - unit->Orders[0].Goal=NoUnitP; - - RefsDebugCheck( !destu->Refs ); - --destu->Refs; - RefsDebugCheck( !destu->Refs ); - - if( i==PF_UNREACHABLE ) { - // FIXME: could try another depot, or retry later. - DebugLevel2Fn("WOOD-DEPOSIT NOT REACHED %d=%d,%d ? %d\n" - _C_ UnitNumber(destu) _C_ destu->X _C_ destu->Y - _C_ MapDistanceToUnit(unit->X,unit->Y,destu)); - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; - return 0; - } - - RemoveUnit(unit,destu); -#if 0 - // FIXME: this breaks the drop out code. - // FIXME: this is a hack, but solves the problem, a better solution is - // FIXME: still wanted. - - // Place unit where pathfinder is more likely to work - if (unit->X < destu->X) { - PlaceUnit(unit,destu->X,unit->Y); - RemoveUnit(unit,NULL); // Unit removal necessary to free map tiles - } - if (unit->X > destu->X+destu->Type->TileWidth-1) { - PlaceUnit(unit,destu->X+destu->Type->TileWidth-1,unit->Y); - RemoveUnit(unit,NULL); - } - if (unit->Y < destu->Y) { - PlaceUnit(unit,unit->X,destu->Y); - RemoveUnit(unit,NULL); - } - if (unit->Y > destu->Y+destu->Type->TileHeight-1) { - PlaceUnit(unit,unit->X,destu->Y+destu->Type->TileHeight-1); - RemoveUnit(unit,NULL); - } -#else - unit->X=destu->X; - unit->Y=destu->Y; -#endif - - // - // Update wood. - // - unit->Player->Resources[WoodCost]+=unit->Player->Incomes[WoodCost]; - unit->Player->TotalResources[WoodCost]+=unit->Player->Incomes[WoodCost]; - if( unit->Player==ThisPlayer ) { - MustRedraw|=RedrawResources; - } - - unit->Player->UnitTypesCount[unit->Type->Type]--; - if( unit->Type==UnitTypeOrcWorkerWithWood ) { - unit->Type=UnitTypeOrcWorker; - } else if( unit->Type==UnitTypeHumanWorkerWithWood ) { - unit->Type=UnitTypeHumanWorker; - } else { - // FIXME: must support more races. - DebugLevel0Fn("Wrong unit for returning wood %d\n" _C_ unit->Type->Type); - } - unit->Player->UnitTypesCount[unit->Type->Type]++; - - unit->Wait=WAIT_FOR_WOOD; - - return 1; -} - -/** -** Wait in wood deposit. -** -** @param unit Pointer to worker unit. -*/ -local int WaitInWoodDeposit(Unit* unit) -{ - Unit* destu; - - DebugLevel3Fn("Waiting\n"); - - // - // Drop out unit at nearest point to target. - // - destu=ResourceDepositOnMap(unit->X,unit->Y,WoodCost); - DebugCheck( !destu ); // there must be a depot! - - DropOutNearest(unit - ,unit->Orders[0].X,unit->Orders[0].Y - ,destu->Type->TileWidth,destu->Type->TileHeight); - - // - // Return to chop point. - // - DebugCheck( unit->Orders[0].Action!=UnitActionHarvest ); - unit->Orders[0].Goal=NoUnitP; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=0; - // NOTE: unit->Orders[0].X && unit->Orders[0].Y holds return place. - NewResetPath(unit); - - CheckUnitToBeDrawn(unit); - unit->Wait=1; - //unit->Data.Harvest.WoodToHarvest=CHOP_FOR_WOOD; - unit->Value=CHOP_FOR_WOOD; - return 1; -} - -/** -** Unit Harvest: -** Move into forest. -** Chop the tree. -** Return to wood store. -** Deliver wood. -** Restart from beginning. -** -** FIXME: we should write a generic resource function -** -** @param unit Pointer to worker unit. -*/ -global void HandleActionHarvest(Unit* unit) -{ - switch( unit->SubAction ) { - case 0: - NewResetPath(unit); - unit->Value=CHOP_FOR_WOOD; - unit->SubAction=1; - case 1: - if( MoveToWood(unit) ) { - unit->SubAction=64; - } - break; - - case 64: - if( ChopWood(unit) ) { - unit->SubAction=128; - } - break; - - case 128: - if( ReturnWithWood(unit) ) { - unit->SubAction=192; - } - break; - - case 192: - if( WaitInWoodDeposit(unit) ) { - unit->SubAction=0; - } - break; - } -} - -//@} diff --git a/src/action/action_move.cpp b/src/action/action_move.cpp index b90198ba7..3e2e8c6c2 100644 --- a/src/action/action_move.cpp +++ b/src/action/action_move.cpp @@ -78,6 +78,8 @@ local int ActionMoveGeneric(Unit* unit,const Animation* anim) // FIXME: state 0?, should be wrong, should be Reset. // FIXME: Reset flag is cleared by HandleUnitAction. if( !(state=unit->State) ) { + // FIXME: So units flying up and down are not affected. + unit->IX=unit->IY=0; #ifdef HIERARCHIC_PATHFINDER d = PfHierComputePath (unit, &xd, &yd); diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp index 7c2ddd832..6c77361ff 100644 --- a/src/action/action_resource.cpp +++ b/src/action/action_resource.cpp @@ -50,7 +50,7 @@ ----------------------------------------------------------------------------*/ #define SUB_START_RESOURCE 0 -#define SUB_MOVE_TO_RESOURCE 1 +#define SUB_MOVE_TO_RESOURCE 5 #define SUB_UNREACHABLE_RESOURCE 31 #define SUB_START_GATHERING 55 #define SUB_GATHER_RESOURCE 60 @@ -73,24 +73,62 @@ local int MoveToResource(Unit* unit) { Unit* goal; + ResourceInfo* resinfo; + int x; + int y; - goal=unit->Orders[0].Goal; - DebugCheck( !goal ); - - switch( DoActionMove(unit) ) { // reached end-point? - case PF_UNREACHABLE: - return -1; - case PF_REACHED: - break; - default: - if( !unit->Reset || !(goal->Destroyed || goal->Removed - || !goal->HP || goal->Orders[0].Action==UnitActionDie) ) { - return 0; + resinfo=unit->Type->ResInfo[unit->CurrentResource]; + if (resinfo->TerrainHarvester) { + x=unit->Orders->X; + y=unit->Orders->Y; + // Wood gone, look somewhere else. + if ( (!ForestOnMap(x,y)) && (!unit->IX) && (!unit->IY)) { + if (!FindTerrainType(UnitMovementMask(unit),MapFieldForest,0,10, + unit->Player,unit->X,unit->Y,&x,&y)) { + DebugLevel3Fn("No wood in range\n"); + return -1; + } else { + DebugLevel3Fn("%d,%d -> %d,%d\n" _C_ unit->X _C_ unit->Y _C_ x _C_ y); + unit->Orders->X=x; + unit->Orders->Y=y; + NewResetPath(unit); } - break; + } + switch( DoActionMove(unit)) { + case PF_UNREACHABLE: + if (FindTerrainType(UnitMovementMask(unit),MapFieldForest,0,9999,unit->Player,unit->X,unit->Y,&x,&y)) { + unit->Orders->X=x; + unit->Orders->Y=y; + NewResetPath(unit); + DebugLevel3Fn("Found a better place to harvest %d,%d\n" _C_ x _C_ y); + // FIXME: can't this overflow? It really shouldn't, since + // x and y are really supossed to be reachable, checked thorugh a flood fill. + return MoveToResource(unit); + } + return -1; + case PF_REACHED: + return 1; + default: + return 0; + } + } else { + goal=unit->Orders[0].Goal; + DebugCheck( !goal ); + switch( DoActionMove(unit) ) { // reached end-point? + case PF_UNREACHABLE: + return -1; + case PF_REACHED: + break; + default: + // Goal gone or something. + if( !unit->Reset || !(goal->Destroyed || goal->Removed + || !goal->HP || goal->Orders[0].Action==UnitActionDie) ) { + return 0; + } + break; + } + return 1; } - - return 1; } /* @@ -103,13 +141,30 @@ local int MoveToResource(Unit* unit) local int StartGathering(Unit* unit) { Unit * goal; + ResourceInfo* resinfo; + + resinfo=unit->Type->ResInfo[unit->CurrentResource]; + DebugCheck(unit->IX); + DebugCheck(unit->IY); + if (resinfo->TerrainHarvester) { + DebugCheck(!ForestOnMap(unit->Orders->X,unit->Orders->Y)); + UnitHeadingFromDeltaXY(unit,unit->Orders->X-unit->X,unit->Orders->Y-unit->Y); + if (resinfo->WaitAtResource) { + unit->Data.ResWorker.TimeToHarvest=resinfo->WaitAtResource; + } else { + unit->Data.ResWorker.TimeToHarvest=1; + } + unit->Data.ResWorker.DoneHarvesting=0; + return 1; + } + goal=unit->Orders[0].Goal; // // Target is dead, stop getting resources. // if( goal->Destroyed || goal->Removed || !goal->HP || goal->Orders[0].Action==UnitActionDie) { - DebugLevel0Fn("Destroyed resource goal, stop gathering.\n"); + DebugLevel3Fn("Destroyed resource goal, stop gathering.\n"); RefsDebugCheck( !goal->Refs ); --goal->Refs; if( goal->Destroyed ) { @@ -119,15 +174,23 @@ local int StartGathering(Unit* unit) } else { RefsDebugCheck( !goal->Refs ); } - unit->Orders[0].Goal=NoUnitP; + // Find an alternative, but don't look too far. unit->Orders[0].X=unit->Orders[0].Y=-1; - // FIXME: Choose an alternative - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; + if ((goal=FindResource(unit,unit->X,unit->Y,10))) { + unit->SubAction=SUB_START_RESOURCE; + unit->Orders[0].Goal=goal; + RefsDebugCheck( !goal->Refs ); + ++goal->Refs; + } else { + unit->Orders[0].Action=UnitActionStill; + unit->Orders[0].Goal=0; + unit->SubAction=0; + } return 0; } // FIXME: 0 can happen, if to near placed by map designer. + DebugLevel3Fn("%d\n" _C_ MapDistanceToUnit(unit->X,unit->Y,goal) ); DebugCheck( MapDistanceToUnit(unit->X,unit->Y,goal)>1 ); // @@ -163,7 +226,7 @@ local int StartGathering(Unit* unit) // // Place unit inside the resource // - if (!unit->Type->HarvestFromOutside) { + if (!resinfo->HarvestFromOutside) { RefsDebugCheck( !goal->Refs ); --goal->Refs; @@ -175,13 +238,9 @@ local int StartGathering(Unit* unit) unit->Y=goal->Y; } - if (unit->Type->WaitAtResource) { - unit->Data.ResWorker.TimeToHarvest=unit->Type->WaitAtResource; - } else { - unit->Data.ResWorker.TimeToHarvest=1; - } + unit->Data.ResWorker.TimeToHarvest=resinfo->WaitAtResource; - unit->Reset=1; + unit->Data.ResWorker.DoneHarvesting=0; return 1; } @@ -196,11 +255,7 @@ local void AnimateActionHarvest(Unit* unit) flags=UnitShowAnimation(unit,unit->Type->Animations->Attack); #ifdef WITH_SOUND if( (flags&AnimationSound) ) { - if( GameSounds.Repair.Sound==(void*)-1 ) { - PlayUnitSound(unit,VoiceAttacking); - } else { - PlayUnitSound(unit,VoiceRepair); - } + PlayUnitSound(unit,VoiceTreeChopping); } #endif } @@ -218,133 +273,171 @@ local int GatherResource(Unit* unit) Unit* source; Unit* depot; Unit* uins; + ResourceInfo* resinfo; int i; int addload; - - if (unit->Type->HarvestFromOutside) { + + resinfo=unit->Type->ResInfo[unit->CurrentResource]; + source=0; + + if (resinfo->HarvestFromOutside||resinfo->TerrainHarvester) { AnimateActionHarvest(unit); + unit->Data.ResWorker.TimeToHarvest-=unit->Wait; } else { + unit->Data.ResWorker.TimeToHarvest--; unit->Wait=1; } - // This means we have to return. - if (unit->Data.ResWorker.TimeToHarvest==-1) { - DebugCheck(!unit->Type->HarvestFromOutside); + if (unit->Data.ResWorker.DoneHarvesting) { + DebugCheck(!(resinfo->HarvestFromOutside||resinfo->TerrainHarvester)); return unit->Reset; } - if (--unit->Data.ResWorker.TimeToHarvest) { - DebugLevel3Fn("need to wait %d more cycles.\n" _C_ unit->Data.ResWorker.TimeToHarvest); + // Target gone? + if (resinfo->TerrainHarvester&&!ForestOnMap(unit->Orders->X,unit->Orders->Y)) { + DebugLevel3Fn("Wood gone for unit %d.\n" _C_ unit->Slot); + if (unit->Reset) { + // Action now breakable, move to resource again. + unit->SubAction=SUB_MOVE_TO_RESOURCE; + // Give it some reasonable look while serching. + unit->Frame=unit->Type->Animations->Still->Frame; + } return 0; - } - - unit->Data.ResWorker.TimeToHarvest=unit->Type->WaitAtResource; - - if ( unit->Type->HarvestFromOutside ) { - source=unit->Orders[0].Goal; - } else { - source=unit->Container; + // No wood? Freeze!!! } - DebugCheck( !source ); - DebugCheck( source->Value>655350 ); - - // - // Target is dead, stop getting resources. - // - if( !(source->Destroyed || source->Removed || !source->HP || - source->Orders[0].Action==UnitActionDie)) { + if (!unit->Data.ResWorker.DoneHarvesting&&unit->Data.ResWorker.TimeToHarvest<0) { + unit->Data.ResWorker.TimeToHarvest+=resinfo->WaitAtResource; + // // Calculate how much we can load. - // - if (unit->Type->ResourceStep) { - addload = unit->Type->ResourceStep; + // + if (resinfo->ResourceStep) { + addload = resinfo->ResourceStep; } else { - addload = unit->Type->ResourceCapacity; - } - // Don't load more that there is. - if (addload > source->Value) { - addload = source->Value; + addload = resinfo->ResourceCapacity; } // Make sure we don't bite more than we can chew. - if (unit->Value + addload > unit->Type->ResourceCapacity) { - addload = unit->Type->ResourceCapacity - unit->Value; + if (unit->Value + addload > resinfo->ResourceCapacity) { + addload = resinfo->ResourceCapacity - unit->Value; } - - DebugLevel3Fn("Harvested another %d resources.\n" _C_ addload); - unit->Value += addload; - source->Value -= addload; - - UnitMarkSeen(source); - if( IsOnlySelected(source) ) { - MustRedraw|=RedrawInfoPanel; - } - // Change unit to full state. - if( unit->Type->TransformWhenLoaded&&unit->Value ) { - unit->Player->UnitTypesCount[unit->Type->Type]--; - unit->Type=unit->Type->TransformWhenLoaded; - unit->Player->UnitTypesCount[unit->Type->Type]++; - } - } - - // - // End of resource: destroy the resource. - // FIXME: implement depleted resources. - // - if( source->Destroyed || source->Removed || !source->HP || - source->Orders[0].Action==UnitActionDie || source->Value==0) { - DebugLevel0Fn("Resource is destroyed\n"); - uins=source->UnitInside; - // - // Improved version of DropOutAll that makes workers go to the depot. - // FIXME: empty harvesters whould find another resource. - // - for( i=source->InsideCount; i; --i,uins=uins->NextContained ) { - if (uins->Value && (depot=FindDeposit(uins->Player,uins->X,uins->Y,source->Type->GivesResource))) { - DropOutNearest(uins,depot->X+depot->Type->TileWidth/2 - ,depot->Y+depot->Type->TileHeight/2 - ,source->Type->TileWidth,source->Type->TileHeight); - uins->Orders[0].Action=UnitActionReturnGoods; - uins->SubAction=0; - uins->Wait=unit->Reset=1; - uins->Orders[0].Goal=depot; - - RefsDebugCheck( !depot->Refs ); - ++depot->Refs; - DebugLevel0Fn("Sent unit %d to depot\n" _C_ uins->Slot); - continue; + if (resinfo->TerrainHarvester) { + DebugLevel3Fn("Harvested another %d resources.\n" _C_ addload); + unit->Value += addload; + + if (addload&&unit->Value==resinfo->ResourceCapacity) { + DebugLevel3("Removed wood.\n"); + MapRemoveWood(unit->Orders->X,unit->Orders->Y); } - DebugLevel0Fn("Unit %d just sits around confused.\n" _C_ uins->Slot); - DropOutOnSide(uins,LookingW - ,source->Type->TileWidth,source->Type->TileHeight); - uins->Orders[0].Goal=0; - uins->Orders[0].X=uins->Orders[0].Y=-1; - uins->Orders[0].Action=UnitActionStill; - uins->Wait=unit->Reset=1; - uins->SubAction=0; - } - - // Don't destroy the resource twice. - if( !(source->Destroyed || source->Removed || !source->HP || - source->Orders[0].Action==UnitActionDie)){ - LetUnitDie(source); - } - // FIXME: make the workers inside look for a new resource. - source=NULL; - } - - // Only return home if we are full. - if (unit->Value < unit->Type->ResourceCapacity || !source) { - return 0; - } else { - if (unit->Type->HarvestFromOutside) { - unit->Data.ResWorker.TimeToHarvest=-1; - return 0; } else { - return 1; + if ( resinfo->HarvestFromOutside ) { + source=unit->Orders[0].Goal; + } else { + source=unit->Container; + } + + DebugCheck( !source ); + DebugCheck( source->Value>655350 ); + + // + // Target is dead, stop getting resources. + // + if( !(source->Destroyed || source->Removed || !source->HP || + source->Orders[0].Action==UnitActionDie)) { + // Don't load more that there is. + if (addload > source->Value) { + addload = source->Value; + } + + DebugLevel3Fn("Harvested another %d resources.\n" _C_ addload); + unit->Value += addload; + source->Value -= addload; + + UnitMarkSeen(source); + if( IsOnlySelected(source) ) { + MustRedraw|=RedrawInfoPanel; + } + } + + // + // End of resource: destroy the resource. + // FIXME: implement depleted resources. + // + if( source->Destroyed || source->Removed || !source->HP || + source->Orders[0].Action==UnitActionDie || source->Value==0) { + DebugLevel0Fn("Resource is destroyed\n"); + uins=source->UnitInside; + // + // Improved version of DropOutAll that makes workers go to the depot. + // FIXME: empty harvesters should find another resource. + // + for( i=source->InsideCount; i; --i,uins=uins->NextContained ) { + if (uins->Value && (depot=FindDeposit(uins,uins->X,uins->Y,1000))) { + DropOutNearest(uins,depot->X+depot->Type->TileWidth/2 + ,depot->Y+depot->Type->TileHeight/2 + ,source->Type->TileWidth,source->Type->TileHeight); + // Remember were it mined, so it can look around for another resource. + uins->Orders[0].Arg1=(void*)((unit->X<<16)|unit->Y); + uins->Orders[0].Goal=depot; + RefsDebugCheck( !depot->Refs ); + ++depot->Refs; + NewResetPath(uins); + uins->SubAction=SUB_MOVE_TO_DEPOT; + uins->Wait=1; + DebugLevel0Fn("Sent unit %d to depot\n" _C_ uins->Slot); + continue; + } + DropOutOnSide(uins,LookingW + ,source->Type->TileWidth,source->Type->TileHeight); + uins->Orders[0].X=uins->Orders[0].Y=-1; + if ((uins->Orders[0].Goal=FindResource(uins,uins->X,uins->Y,10))) { + DebugLevel0Fn("Unit %d found another resource.\n" _C_ uins->Slot); + uins->SubAction=SUB_START_RESOURCE; + uins->Wait=1; + RefsDebugCheck( !uins->Orders[0].Goal->Refs ); + ++uins->Orders[0].Goal->Refs; + } else { + DebugLevel0Fn("Unit %d just sits around confused.\n" _C_ uins->Slot); + uins->Orders[0].Action=UnitActionStill; + uins->SubAction=0; + uins->Wait=unit->Reset=1; + } + } + + // Don't destroy the resource twice. + if( !(source->Destroyed || source->Removed || !source->HP || + source->Orders[0].Action==UnitActionDie)){ + LetUnitDie(source); + // FIXME: make the workers inside look for a new resource. + } + source=NULL; + } + } + if (resinfo->TerrainHarvester) { + if (unit->Value == resinfo->ResourceCapacity) { + // Mark as complete. + DebugLevel3Fn("Done Harvesting, waiting for reset.\n"); + unit->Data.ResWorker.DoneHarvesting=1; + } + return 0; + } + + if (resinfo->HarvestFromOutside && !resinfo->TerrainHarvester) { + if ((unit->Value == resinfo->ResourceCapacity)||(source==NULL)) { + // Mark as complete. + DebugLevel3Fn("Done Harvesting, waiting for reset %X.\n" _C_ (unsigned)source); + unit->Data.ResWorker.DoneHarvesting=1; + } + return 0; + } + + if ((!resinfo->HarvestFromOutside)&& (!resinfo->TerrainHarvester)) { + return unit->Value==resinfo->ResourceCapacity&&source; } } + + return 0; } /** @@ -358,25 +451,31 @@ local int StopGathering(Unit* unit) { Unit* depot; Unit* source; + ResourceInfo* resinfo; - - if ( unit->Type->HarvestFromOutside ) { - source=unit->Orders[0].Goal; - } else { - source=unit->Container; + resinfo=unit->Type->ResInfo[unit->CurrentResource]; + + + source=0; + if (!resinfo->TerrainHarvester) { + if ( resinfo->HarvestFromOutside ) { + source=unit->Orders[0].Goal; + } else { + source=unit->Container; + } + source->Data.Resource.Active--; + DebugCheck(source->Data.Resource.Active<0); } - source->Data.Resource.Active--; - DebugCheck(source->Data.Resource.Active<0); // Store resource position. // FIXME: is this the best way? unit->Orders[0].Arg1=(void*)((unit->X<<16)|unit->Y); // Find and send to resource deposit. - if( (!(depot=FindDeposit(unit->Player,unit->X,unit->Y,unit->Type->ResourceHarvested))) + if( (!(depot=FindDeposit(unit,unit->X,unit->Y,1000))) || (!unit->Value)) { - if (!unit->Type->HarvestFromOutside) { + if (!(resinfo->HarvestFromOutside||resinfo->TerrainHarvester)) { DebugCheck(!unit->Container); DropOutOnSide(unit,LookingW,source->Type->TileWidth,source->Type->TileHeight); } @@ -385,7 +484,7 @@ local int StopGathering(Unit* unit) unit->SubAction=0; // should return 0, done below! } else { - if (!unit->Type->HarvestFromOutside) { + if (!(resinfo->HarvestFromOutside||resinfo->TerrainHarvester)) { DebugCheck(!unit->Container); DropOutNearest(unit,depot->X+depot->Type->TileWidth/2 ,depot->Y+depot->Type->TileHeight/2 @@ -421,6 +520,9 @@ local int StopGathering(Unit* unit) local int MoveToDepot(Unit* unit) { Unit* goal; + ResourceInfo* resinfo; + + resinfo=unit->Type->ResInfo[unit->CurrentResource]; goal=unit->Orders[0].Goal; DebugCheck( !goal ); @@ -489,29 +591,17 @@ local int MoveToDepot(Unit* unit) // // Update resource. // - unit->Player->Resources[unit->Type->ResourceHarvested]+= - (unit->Value*unit->Player->Incomes[unit->Type->ResourceHarvested])/100; - unit->Player->TotalResources[unit->Type->ResourceHarvested]+= - (unit->Value*unit->Player->Incomes[unit->Type->ResourceHarvested])/100; + unit->Player->Resources[resinfo->FinalResource]+= + (unit->Value*unit->Player->Incomes[resinfo->FinalResource])/100; + unit->Player->TotalResources[resinfo->FinalResource]+= + (unit->Value*unit->Player->Incomes[resinfo->FinalResource])/100; unit->Value=0; if( unit->Player==ThisPlayer ) { MustRedraw|=RedrawResources; } - // - // Change unit to empty state. - // - if( unit->Type->TransformWhenEmpty ) { - unit->Player->UnitTypesCount[unit->Type->Type]--; - unit->Type=unit->Type->TransformWhenEmpty; - unit->Player->UnitTypesCount[unit->Type->Type]++; - } + unit->Wait=resinfo->WaitAtDepot; - if (unit->Type->WaitAtDepot) { - unit->Wait=unit->Type->WaitAtDepot; - } else { - unit->Wait=1; - } return 1; } @@ -526,10 +616,13 @@ local int WaitInDepot(Unit* unit) { const Unit* depot; Unit* goal; + ResourceInfo* resinfo; int x; int y; - depot=ResourceDepositOnMap(unit->X,unit->Y,unit->Type->ResourceHarvested); + resinfo=unit->Type->ResInfo[unit->CurrentResource]; + + depot=ResourceDepositOnMap(unit->X,unit->Y,resinfo->ResourceId); DebugCheck( !depot ); // Could be destroyed, but then we couldn't be in? @@ -540,20 +633,33 @@ local int WaitInDepot(Unit* unit) x=(int)unit->Orders[0].Arg1>>16; y=(int)unit->Orders[0].Arg1&0xFFFF; } - if( !(goal=FindResource(unit->Player,x,y,unit->Type->ResourceHarvested)) ) { - DropOutOnSide(unit,LookingW, - depot->Type->TileWidth,depot->Type->TileHeight); - unit->Orders[0].Action=UnitActionStill; - unit->SubAction=0; + // Range hardcoded. don't stray too far though + if( resinfo->TerrainHarvester ) { + if (FindTerrainType(UnitMovementMask(unit),MapFieldForest,0,10, + unit->Player,x,y,&x,&y)) { + DropOutNearest(unit,x,y,depot->Type->TileWidth,depot->Type->TileHeight); + unit->Orders->X=x; + unit->Orders->Y=y; + } else { + DropOutOnSide(unit,LookingW,depot->Type->TileWidth,depot->Type->TileHeight); + unit->Orders[0].Action=UnitActionStill; + unit->SubAction=0; + } } else { - DropOutNearest(unit,goal->X+goal->Type->TileWidth/2 - ,goal->Y+goal->Type->TileHeight/2 - ,depot->Type->TileWidth,depot->Type->TileHeight); - unit->Orders[0].Goal=goal; - RefsDebugCheck( !goal->Refs ); - ++goal->Refs; - unit->Orders[0].RangeX=unit->Orders[0].RangeY=1; - unit->Orders[0].X=unit->Orders[0].Y=-1; + if ((goal=FindResource(unit,x,y,10))) { + DropOutNearest(unit,goal->X+goal->Type->TileWidth/2 + ,goal->Y+goal->Type->TileHeight/2 + ,depot->Type->TileWidth,depot->Type->TileHeight); + unit->Orders[0].Goal=goal; + RefsDebugCheck( !goal->Refs ); + ++goal->Refs; + unit->Orders[0].RangeX=unit->Orders[0].RangeY=1; + unit->Orders[0].X=unit->Orders[0].Y=-1; + } else { + DropOutOnSide(unit,LookingW,depot->Type->TileWidth,depot->Type->TileHeight); + unit->Orders[0].Action=UnitActionStill; + unit->SubAction=0; + } } CheckUnitToBeDrawn(unit); @@ -574,6 +680,10 @@ void ResourceGiveUp(Unit* unit) unit->Reset=1; unit->Orders[0].X=unit->Orders[0].Y=-1; unit->SubAction=0; + if( unit->Type->ResInfo[unit->CurrentResource]->LoseResources ) { + unit->Value=0; + unit->CurrentResource=0; + } if( unit->Orders[0].Goal ) { RefsDebugCheck( !unit->Orders[0].Goal->Refs ); --unit->Orders[0].Goal->Refs; @@ -591,13 +701,23 @@ void ResourceGiveUp(Unit* unit) */ global void HandleActionResource(Unit* unit) { - int ret; + int ret,newres; DebugLevel3Fn("%s(%d) SubAction %d\n" _C_ unit->Type->Ident _C_ UnitNumber(unit) _C_ unit->SubAction); - + // Let's start mining. if ( unit->SubAction==SUB_START_RESOURCE ) { + if (unit->Orders->Goal) { + newres=unit->Orders->Goal->Type->GivesResource; + } else { + newres=WoodCost; + } + if (newres!=unit->CurrentResource) { + // Drop other resources. + unit->Value=0; + } + unit->CurrentResource=newres; NewResetPath(unit); DebugLevel3Fn("Started mining. reset path.\n"); unit->SubAction=SUB_MOVE_TO_RESOURCE; @@ -632,8 +752,9 @@ global void HandleActionResource(Unit* unit) if (unit->SubAction==SUB_START_GATHERING) { if (StartGathering(unit)) { unit->SubAction=SUB_GATHER_RESOURCE; - } + } else { return; + } } // Gather the resource. diff --git a/src/action/action_returngoods.cpp b/src/action/action_returngoods.cpp index aff10d08d..15d08a6fb 100644 --- a/src/action/action_returngoods.cpp +++ b/src/action/action_returngoods.cpp @@ -54,9 +54,9 @@ /** ** Return goods to gold/wood deposit. ** -** FIXME: must support to move to a specified deposit. -** ** @param unit pointer to unit. +** +** FIXME: move this into action_resource? */ global void HandleActionReturnGoods(Unit* unit) { @@ -65,56 +65,30 @@ global void HandleActionReturnGoods(Unit* unit) type=unit->Type; // - // Select target to return goods. FIXME: more races support + // Select target to return goods. // - if( type->Harvester ) { - if( !unit->Orders[0].Goal ) { - if( !(destu=FindDeposit(unit->Player,unit->X,unit->Y,type->ResourceHarvested)) ) { - DebugLevel3Fn("No deposit -> can't return\n"); - unit->Orders[0].Action=UnitActionStill; - return; - } - unit->Orders[0].Goal=destu; - RefsDebugCheck( !destu->Refs ); - ++destu->Refs; + DebugCheck(!type->Harvester ); + if( !unit->Orders[0].Goal ) { + if( !(destu=FindDeposit(unit,unit->X,unit->Y,1000)) ) { + DebugLevel3Fn("No deposit -> can't return\n"); + unit->Orders[0].Action=UnitActionStill; + return; } - DebugLevel3("Return to %d=%d,%d\n" - _C_ UnitNumber(unit->Orders[0].Goal) - _C_ unit->Orders[0].X _C_ unit->Orders[0].Y); - unit->Orders[0].Action=UnitActionResource; - // Somewhere on the way the loaded worker changed Arg1. - // Bummer, go get the closest resource to the depot - unit->Orders[0].Arg1=(void*)-1; - NewResetPath(unit); - unit->SubAction=70; - unit->Wait=1; - return; - } - - if( type==UnitTypeHumanWorkerWithWood || type==UnitTypeOrcWorkerWithWood ) { - if( !unit->Orders[0].Goal ) { - if( !(destu=FindDeposit(unit->Player,unit->X,unit->Y,WoodCost)) ) { - // No deposit -> can't return - unit->Orders[0].Action=UnitActionStill; - return; - } - unit->Orders[0].Goal=destu; - RefsDebugCheck( !destu->Refs ); - ++destu->Refs; - } - unit->Orders[0].X=unit->X; - unit->Orders[0].Y=unit->Y; // Return point to continue. - DebugLevel3("Return to %d=%d,%d\n" - _C_ UnitNumber(unit->Orders[0].Goal) - _C_ unit->Orders[0].X _C_ unit->Orders[0].Y); - unit->Orders[0].Action=UnitActionHarvest; - unit->Orders[0].Arg1=(void*)-1; - NewResetPath(unit); - unit->SubAction=128; // FIXME: Hardcoded - DebugLevel3("Wait: %d\n" _C_ unit->Wait); - unit->Wait=1; - return; + unit->Orders[0].Goal=destu; + RefsDebugCheck( !destu->Refs ); + ++destu->Refs; } + DebugLevel3("Return to %d=%d,%d\n" + _C_ UnitNumber(unit->Orders[0].Goal) + _C_ unit->Orders[0].X _C_ unit->Orders[0].Y); + unit->Orders[0].Action=UnitActionResource; + // Somewhere on the way the loaded worker could have change Arg1 + // Bummer, go get the closest resource to the depot + unit->Orders[0].Arg1=(void*)-1; + NewResetPath(unit); + unit->SubAction=70; + unit->Wait=1; + return; } //@} diff --git a/src/action/action_train.cpp b/src/action/action_train.cpp index 773c48789..8a078a06f 100644 --- a/src/action/action_train.cpp +++ b/src/action/action_train.cpp @@ -155,9 +155,6 @@ global void HandleActionTrain(Unit* unit) && !nunit->Type->Harvester) || (unit->NewOrder.Action==UnitActionAttack && !nunit->Type->CanAttack) - || ((unit->NewOrder.Action==UnitActionHarvest) - && nunit->Type!=UnitTypeOrcWorker - && nunit->Type!=UnitTypeHumanWorker ) || (unit->NewOrder.Action==UnitActionBoard && nunit->Type->UnitType!=UnitTypeLand) ) { DebugLevel0Fn("Wrong order for unit\n"); diff --git a/src/action/actions.cpp b/src/action/actions.cpp index 891fc7422..14e21e9dc 100644 --- a/src/action/actions.cpp +++ b/src/action/actions.cpp @@ -174,10 +174,10 @@ local void (*HandleActionTable[256])(Unit*) = { HandleActionPatrol, HandleActionBuild, HandleActionRepair, - HandleActionHarvest, HandleActionResource, HandleActionReturnGoods, HandleActionDemolish, + HandleActionNotWritten, // Enough for the future ? HandleActionNotWritten, HandleActionNotWritten, HandleActionNotWritten, @@ -268,7 +268,7 @@ local void (*HandleActionTable[256])(Unit*) = { local void HandleUnitAction(Unit* unit) { int z; - + // // If current action is breakable proceed with next one. // @@ -306,6 +306,11 @@ local void HandleUnitAction(Unit* unit) ReleaseUnit(unit->Orders[0].Goal); } } + if (unit->CurrentResource) { + if (unit->Type->ResInfo[unit->CurrentResource]->LoseResources) { + unit->Value=0; + } + } // // Shift queue with structure assignment. diff --git a/src/action/command.cpp b/src/action/command.cpp index aa9d9fb46..309b648d6 100644 --- a/src/action/command.cpp +++ b/src/action/command.cpp @@ -702,29 +702,23 @@ global void CommandCancelBuilding(Unit* unit, } /** -** Send unit harvest +** Send unit harvest a location ** ** @param unit pointer to unit. ** @param x X map position for harvest. ** @param y Y map position for harvest. ** @param flush if true, flush command queue. */ -global void CommandHarvest(Unit* unit,int x,int y,int flush) +global void CommandResourceLoc(Unit* unit,int x,int y,int flush) { Order* order; + int nx; + int ny; // // Check if unit is still valid? (NETWORK!) // if( !unit->Removed && unit->Orders[0].Action!=UnitActionDie ) { - // FIXME: more races, could happen with many orders in queue. - if( !unit->Type->Building - && unit->Type!=UnitTypeHumanWorker - && unit->Type!=UnitTypeOrcWorker ) { - DebugLevel0Fn("None worker gets order\n"); - ClearSavedAction(unit); - return; - } if( unit->Type->Building ) { // FIXME: should find a better way for pending orders. order=&unit->NewOrder; @@ -733,9 +727,27 @@ global void CommandHarvest(Unit* unit,int x,int y,int flush) return; } - order->Action=UnitActionHarvest; - order->X=x; - order->Y=y; + order->Action=UnitActionResource; + + // Find the closest piece of wood next to a tile where the unit can move + DebugLevel3("Want to harvest from %d,%d.\n" _C_ x _C_ y); + if (!FindTerrainType(0,(unit->Type->MovementMask),1,20,unit->Player,x,y,&nx,&ny)) { + DebugLevel0Fn("FIXME: Give up???\n"); + } + if (max(abs(nx-x),abs(ny-y))>1) { + DebugLevel3("Closest tile reachable is at %d,%d.\n" _C_ x _C_ y); + if (!FindTerrainType(0,MapFieldForest,0,20,unit->Player,nx,ny,&nx,&ny)) { + DebugLevel0Fn("FIXME: Give up???\n"); + } + } else { + // The destination is next to a reacahble tile. + nx=x; + ny=y; + } + DebugLevel3("So the final destination is %d,%d.\n" _C_ nx _C_ ny); + order->X=nx; + order->Y=ny; + order->RangeX=order->RangeY=1; order->Goal=NoUnitP; order->Type=NULL; @@ -806,8 +818,7 @@ global void CommandReturnGoods(Unit* unit,Unit* goal,int flush) // FIXME: more races, could happen with many orders in queue. if( !unit->Type->Building && !unit->Type->Harvester - && unit->Type!=UnitTypeHumanWorkerWithWood - && unit->Type!=UnitTypeOrcWorkerWithWood) { + && !unit->Value ) { ClearSavedAction(unit); return; } diff --git a/src/ai/ai_resource.cpp b/src/ai/ai_resource.cpp index 06f8ea715..446c1f58d 100644 --- a/src/ai/ai_resource.cpp +++ b/src/ai/ai_resource.cpp @@ -744,180 +744,12 @@ local void AiCheckingWork(void) ** ** @return Pointer to the nearest reachable gold mine. ** -** @see FindGoldMine but this version doesn't check for explored tiles. +** @see FindGoldMine */ local Unit* AiFindGoldMine(const Unit* unit) { - static const int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 }; - static const int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 }; - struct { - unsigned short X; - unsigned short Y; - } * points; - int size; - int x; - int y; - int rx; - int ry; - int mask; - int wp; - int rp; - int ep; - int i; - int w; - int n; - unsigned char* m; - unsigned char* matrix; - const Unit* destu; - Unit* mine; - Unit* bestmine; - int destx; - int desty; - int bestx; - int besty; - int bestd; - - destx=x=unit->X; - desty=y=unit->Y; - size=TheMap.Width*TheMap.Height/4; - points=malloc(size*sizeof(*points)); - - // - // Find the nearest gold depot - // - if( (destu=FindDeposit(unit->Player,x,y,GoldCost)) ) { - NearestOfUnit(destu,x,y,&destx,&desty); - } - bestd=99999; - IfDebug( bestx=besty=0; ); // keep the compiler happy - - // - // Make movement matrix. FIXME: can create smaller matrix. - // - matrix=CreateMatrix(); - w=TheMap.Width+2; - matrix+=w+w+2; - - // - // Mark sight range as border. FIXME: matrix didn't need to be bigger. - // - n=unit->Stats->SightRange; - rx=x-n; - if( rx<0 ) { - rx=0; - } - ep=x+n; - if( ep>TheMap.Width ) { - ep=TheMap.Width; - } - ry=y-n; - if( ry<0 ) { - ry=0; - } - wp=y+n; - if( wp>TheMap.Height ) { - wp=TheMap.Height; - } - for( i=rx; i<ep; ++i ) { // top bottom line - matrix[i+ry*w]=matrix[i+wp*w]=66; - } - for( i=ry+1; i<wp-1; ++i ) { - matrix[rx+i*w]=matrix[ep+i*w]=66; - } - -#if 0 - matrix[x+n+(y+n)*w]=matrix[x-n+(y+n)*w]= - matrix[x+n+(y-n)*w]=matrix[x-n+(y-n)*w]=66; - for( i=n; i--; ) { - // FIXME: marks out of map area - DebugCheck( x-i+(y-n)*w<0 || x+i+(y+n)*w>w*TheMap.Hight ); - matrix[x+n+(y+i)*w]=matrix[x-n+(y+i)*w]= - matrix[x+n+(y-i)*w]=matrix[x-n+(y-i)*w]= - matrix[x-i+(y+n)*w]=matrix[x+i+(y+n)*w]= - matrix[x-i+(y-n)*w]=matrix[x+i+(y-n)*w]=66; - } -#endif - - mask=UnitMovementMask(unit); - - points[0].X=x; - points[0].Y=y; - rp=0; - matrix[x+y*w]=1; // mark start point - ep=wp=1; // start with one point - bestmine=NoUnitP; - - // - // Pop a point from stack, push all neighbors which could be entered. - // - for( ;; ) { - while( rp!=ep ) { - rx=points[rp].X; - ry=points[rp].Y; - for( i=0; i<8; ++i ) { // mark all neighbors - x=rx+xoffset[i]; - y=ry+yoffset[i]; - m=matrix+x+y*w; - if( *m ) { // already checked - continue; - } - - // - // Look if there is a mine - // - if ( (mine=ResourceOnMap(x,y,GoldCost)) ) { - if( destu ) { - n=max(abs(destx-x),abs(desty-y)); - if( n<bestd ) { - bestd=n; - bestx=x; - besty=y; - bestmine=mine; - } - *m=22; - } else { // no goal take the first - free(points); - return mine; - } - } - - if( CanMoveToMask(x,y,mask) ) { // reachable - *m=1; - points[wp].X=x; // push the point - points[wp].Y=y; - if( ++wp>=size ) { // round about - wp=0; - } - } else { // unreachable - *m=99; - } - } - if( ++rp>=size ) { // round about - rp=0; - } - } - - // - // Take best of this frame, if any. - // - if( bestd!=99999 ) { - free(points); - return bestmine; - } - - // - // Continue with next frame. - // - if( rp==wp ) { // unreachable, no more points available - break; - } - ep=wp; - } - - DebugLevel3Fn("no mine in sight-range\n"); - - free(points); - return NoUnitP; + // FIXME: explored tiles? + return FindResource(unit,unit->X,unit->Y,100); } /** @@ -939,113 +771,12 @@ local int AiMineGold(Unit* unit) _C_ unit->Type->Ident _C_ unit->X _C_ unit->Y); return 0; } - DebugCheck(unit->Type != UnitTypeHumanWorker - && unit->Type != UnitTypeOrcWorker); CommandResource(unit, dest, FlushCommands); return 1; } -#if 0 - -/** -** Assign worker to harvest. -** -** @param unit Find wood for this worker. -*/ -local int AiHarvest(Unit * unit) -{ - int x, y, addx, addy, i, n, r, wx, wy, bestx, besty, cost; - Unit *dest; - - DebugLevel3Fn("%d\n" _C_ UnitNumber(unit)); - x = unit->X; - y = unit->Y; - addx = unit->Type->TileWidth; - addy = unit->Type->TileHeight; - r = TheMap.Width; - if (r < TheMap.Height) { - r = TheMap.Height; - } - - // This is correct, but can this be written faster??? - if ((dest = FindWoodDeposit(unit->Player, x, y))) { - NearestOfUnit(dest, x, y, &wx, &wy); - DebugLevel3("To %d,%d\n" _C_ wx _C_ wy); - } else { - wx = unit->X; - wy = unit->Y; - } - cost = 99999; - IfDebug(bestx = besty = 0; ); // keep the compiler happy - - // FIXME: if we reach the map borders we can go fast up, left, ... - --x; - while (addx <= r && addy <= r) { - for (i = addy; i--; y++) { // go down - if (CheckedForestOnMap(x, y)) { - n = max(abs(wx - x), abs(wy - y)); - DebugLevel3("Distance %d,%d %d\n" _C_ x _C_ y _C_ n); - if (n < cost && PlaceReachable(unit, x-1, y-1, 3)) { - cost = n; - bestx = x; - besty = y; - } - } - } - ++addx; - for (i = addx; i--; x++) { // go right - if (CheckedForestOnMap(x, y)) { - n = max(abs(wx - x), abs(wy - y)); - DebugLevel3("Distance %d,%d %d\n" _C_ x _C_ y _C_ n); - if (n < cost && PlaceReachable(unit, x-1, y-1, 3)) { - cost = n; - bestx = x; - besty = y; - } - } - } - ++addy; - for (i = addy; i--; y--) { // go up - if (CheckedForestOnMap(x, y)) { - n = max(abs(wx - x), abs(wy - y)); - DebugLevel3("Distance %d,%d %d\n" _C_ x _C_ y _C_ n); - if (n < cost && PlaceReachable(unit, x-1, y-1, 3)) { - cost = n; - bestx = x; - besty = y; - } - } - } - ++addx; - for (i = addx; i--; x--) { // go left - if (CheckedForestOnMap(x, y)) { - n = max(abs(wx - x), abs(wy - y)); - DebugLevel3("Distance %d,%d %d\n" _C_ x _C_ y _C_ n); - if (n < cost && PlaceReachable(unit, x-1, y-1, 3)) { - cost = n; - bestx = x; - besty = y; - } - } - } - if (cost != 99999) { - DebugLevel3Fn("wood on %d,%d\n" _C_ x _C_ y); - DebugCheck(unit->Type!=UnitTypeHumanWorker && unit->Type!=UnitTypeOrcWorker); - CommandHarvest(unit, bestx, besty,FlushCommands); - return 1; - } - ++addy; - } - - DebugLevel0Fn("no wood reachable by %s(%d,%d)\n"); - _C_ unit->Type->Ident _C_ unit->X _C_ unit->Y); - return 0; -} - -#else - /** ** Assign worker to harvest. ** @@ -1090,7 +821,7 @@ local int AiHarvest(Unit * unit) // // Find the nearest wood depot // - if( (destu=FindDeposit(unit->Player,x,y,WoodCost)) ) { + if( (destu=FindDeposit(unit,x,y,100)) ) { NearestOfUnit(destu,x,y,&destx,&desty); } bestd=99999; @@ -1139,9 +870,7 @@ local int AiHarvest(Unit * unit) } *m=22; } else { // no goal take the first - DebugCheck(unit->Type!=UnitTypeHumanWorker - && unit->Type!=UnitTypeOrcWorker); - CommandHarvest(unit,x,y,FlushCommands); + CommandResourceLoc(unit,x,y,FlushCommands); free(points); return 1; } @@ -1167,9 +896,7 @@ local int AiHarvest(Unit * unit) // Take best of this frame, if any. // if( bestd!=99999 ) { - DebugCheck(unit->Type!=UnitTypeHumanWorker - && unit->Type!=UnitTypeOrcWorker); - CommandHarvest(unit, bestx, besty,FlushCommands); + CommandResourceLoc(unit, bestx, besty,FlushCommands); free(points); return 1; } @@ -1190,8 +917,6 @@ local int AiHarvest(Unit * unit) return 0; } -#endif - /** ** Assign worker to haul oil. */ @@ -1200,7 +925,8 @@ local int AiHaulOil(Unit * unit) Unit *dest; DebugLevel3Fn("%d\n" _C_ UnitNumber(unit)); - dest = FindResource(unit->Player, unit->X, unit->Y ,unit->Type->ResourceHarvested); + // Range hardcoded. search the whole map!!! + dest = FindResource(unit, unit->X, unit->Y,1000); if (!dest) { DebugLevel3Fn("oil platform not reachable by %s(%d,%d)\n" _C_ unit->Type->Ident _C_ unit->X _C_ unit->Y); @@ -1261,27 +987,19 @@ local void AiCollectResources(void) if( !unit->Type->Harvester ) { continue; } - c=unit->Type->ResourceHarvested; // // See if it's assigned already // - switch( unit->Orders[0].Action ) { - case UnitActionHarvest: - units_assigned[num_units_assigned[WoodCost]++][WoodCost]=unit; - continue; - case UnitActionResource: - units_assigned[num_units_assigned[c]++][c]=unit; - continue; - default: - break; + if (unit->Orders[0].Action==UnitActionResource) { + c=unit->CurrentResource; + units_assigned[num_units_assigned[c]++][c]=unit; } // // Send workers with resources back home. // - if (unit->Value||unit->Type==UnitTypeHumanWorkerWithWood - ||unit->Type==UnitTypeOrcWorkerWithWood) { + if (unit->Value) { units_with_resource[num_units_with_resource[c]++][c]=unit; if (unit->Orders[0].Action == UnitActionStill && unit->OrderCount==1 ) { @@ -1293,7 +1011,10 @@ local void AiCollectResources(void) // Look what the unit can do // for( c=0; c<MaxCosts; ++c ) { - int tn; + if (unit->Type->ResInfo[c]) { + units_unassigned[num_units_unassigned[c]++][c]=unit; + } +#if 0 UnitType** types; // @@ -1329,6 +1050,7 @@ local void AiCollectResources(void) if( j<tn ) { break; } +#endif } } @@ -1418,7 +1140,7 @@ local void AiCollectResources(void) int n1; int n2; case GoldCost: - if( (unit->Orders[0].Action==UnitActionResource&&unit->Type->ResourceHarvested==GoldCost) || AiMineGold(unit) ) { + if( (unit->Orders[0].Action==UnitActionResource&&unit->CurrentResource==GoldCost) || AiMineGold(unit) ) { DebugLevel3Fn("Assigned to gold\n"); units_assigned[num_units_assigned[c]++][c]=unit; units_unassigned[i][c] = units_unassigned[--num_units_unassigned[c]][c]; @@ -1434,7 +1156,7 @@ local void AiCollectResources(void) } break; case WoodCost: - if( unit->Orders[0].Action==UnitActionHarvest || AiHarvest(unit) ) { + if( (unit->Orders[0].Action==UnitActionResource&&unit->CurrentResource==WoodCost) || AiHarvest(unit) ) { DebugLevel3Fn("Assigned to harvest\n"); units_assigned[num_units_assigned[c]++][c]=unit; units_unassigned[i][c] = units_unassigned[--num_units_unassigned[c]][c]; @@ -1450,7 +1172,7 @@ local void AiCollectResources(void) } break; case OilCost: - if( (unit->Orders[0].Action==UnitActionResource&&unit->Type->ResourceHarvested==OilCost) || AiHaulOil(unit) ) { + if( (unit->Orders[0].Action==UnitActionResource&&unit->CurrentResource==OilCost) || AiHaulOil(unit) ) { DebugLevel3Fn("Assigned to oil\n"); units_assigned[num_units_assigned[c]++][c]=unit; units_unassigned[i][c] = units_unassigned[--num_units_unassigned[c]][c]; diff --git a/src/include/actions.h b/src/include/actions.h index 0f25d6184..b30115b2b 100644 --- a/src/include/actions.h +++ b/src/include/actions.h @@ -100,8 +100,8 @@ extern void CommandUnload(Unit* unit,int x,int y,Unit* what,int flush); extern void CommandBuildBuilding(Unit*,int,int,UnitType*,int); /// Prepare command cancel build extern void CommandCancelBuilding(Unit* unit,Unit* worker); - /// Prepare command harvest -extern void CommandHarvest(Unit* unit,int x,int y,int flush); + /// Prepare command resource location +extern void CommandResourceLoc(Unit* unit,int x,int y,int flush); /// Prepare command resource extern void CommandResource(Unit* unit,Unit* dest,int flush); /// Prepare command return @@ -160,9 +160,7 @@ extern void HandleActionAttack(Unit* unit); extern void HandleActionBoard(Unit* unit); /// Handle command unload extern void HandleActionUnload(Unit* unit); - /// Handle command harvest -extern void HandleActionHarvest(Unit* unit); - /// Handle command haul + /// Handle command resource extern void HandleActionResource(Unit* unit); /// Handle command return extern void HandleActionReturnGoods(Unit* unit); diff --git a/src/include/commands.h b/src/include/commands.h index 7537cf27c..868717ae6 100644 --- a/src/include/commands.h +++ b/src/include/commands.h @@ -98,9 +98,9 @@ extern void SendCommandUnload(Unit* unit,int x,int y,Unit* what,int flush); extern void SendCommandBuildBuilding(Unit*,int,int,UnitType*,int); /// Send cancel building command extern void SendCommandCancelBuilding(Unit* unit,Unit* peon); + /// Send harvest location command +extern void SendCommandResourceLoc(Unit* unit,int x,int y,int flush); /// Send harvest command -extern void SendCommandHarvest(Unit* unit,int x,int y,int flush); - /// Send haul oil command extern void SendCommandResource(Unit* unit,Unit* dest,int flush); /// Send return goods command extern void SendCommandReturnGoods(Unit* unit,Unit* dest,int flush); diff --git a/src/include/iolib.h b/src/include/iolib.h index 8521378f7..1e0d007d3 100644 --- a/src/include/iolib.h +++ b/src/include/iolib.h @@ -47,6 +47,7 @@ #include <bzlib.h> #undef DrawIcon #undef EndMenu +#undef FindResource #endif #ifdef USE_ZZIPLIB diff --git a/src/include/network.h b/src/include/network.h index 6a3a75f14..b92b315cb 100644 --- a/src/include/network.h +++ b/src/include/network.h @@ -83,9 +83,8 @@ enum _message_type_ { MessageCommandUnload, /// Unit command unload MessageCommandBuild, /// Unit command build building MessageCommandCancelBuild, /// Unit command cancel building - MessageCommandHarvest, /// Unit command harvest - MessageCommandMine, /// Unit command mine gold - MessageCommandResource, /// Unit command haul oil + MessageCommandResourceLoc, /// Unit command resource location + MessageCommandResource, /// Unit command resource MessageCommandReturn, /// Unit command return goods MessageCommandTrain, /// Unit command train MessageCommandCancelTrain, /// Unit command cancel training diff --git a/src/include/stratagus.h b/src/include/stratagus.h index 986ad2e67..cc9889675 100644 --- a/src/include/stratagus.h +++ b/src/include/stratagus.h @@ -139,6 +139,7 @@ #pragma warning(disable:4244) // Conversion from double to uchar #pragma warning(disable:4761) // Integral size mismatch #define snprintf _snprintf /// Unix -> dumm +#define vsnprintf _vsnprintf #include <string.h> #define strdup _strdup #define strncasecmp strnicmp @@ -531,21 +532,8 @@ extern long isqrt(long num); /// bits macro #define BitsOf(n) (sizeof(n)*8) - // FIXME: more config stuff which needs a better place - - /// How long stay in a gold-mine -#define MINE_FOR_GOLD (150/SpeedResourcesHarvest[GoldCost]) - /// How long stay in a gold-deposit -#define WAIT_FOR_GOLD (150/SpeedResourcesReturn[GoldCost]) - /// How much I must chop for 1 wood -#define CHOP_FOR_WOOD (52/SpeedResourcesHarvest[WoodCost]) - /// How long stay in a wood-deposit -#define WAIT_FOR_WOOD (100/SpeedResourcesReturn[WoodCost]) - /// How long stay in a oil-well -#define HAUL_FOR_OIL (100/SpeedResourcesHarvest[OilCost]) - /// How long stay in a oil-deposit -#define WAIT_FOR_OIL (100/SpeedResourcesReturn[OilCost]) - +// FIXME: configurable. maybe we could move it into one big global +// FIXME: settings struct? /// How many resource get the player back if canceling building #define CancelBuildingCostsFactor 75 /// How many resource get the player back if canceling training diff --git a/src/include/unit.h b/src/include/unit.h index f3cbc415c..3729e1da2 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -420,7 +420,6 @@ enum _unit_action_ { UnitActionBuild, /// unit builds building UnitActionRepair, /// unit repairing - UnitActionHarvest, /// unit harvest lumber UnitActionResource, /// unit harvesting resources UnitActionReturnGoods, /// unit returning any resource @@ -568,6 +567,7 @@ struct _unit_ { ** ,used for fancy buildings */ unsigned Rs : 8; + unsigned CurrentResource; #define MAX_ORDERS 16 /// How many outstanding orders? char OrderCount; /// how many orders in queue @@ -595,6 +595,7 @@ struct _unit_ { } Resource; /// Resource still struct _order_resource_worker_ { int TimeToHarvest; /// how much time until we harvest some more. + unsigned DoneHarvesting:1; /// Harvesting done, wait for action to break. } ResWorker; /// Worker harvesting struct _order_research_ { Upgrade* Upgrade; /// Upgrade researched @@ -819,12 +820,15 @@ extern int CanBuildOn(int x,int y,int mask); extern int CanBuildUnitType(const Unit* unit,const UnitType* type,int x,int y); /// Find resource -extern Unit* FindResource(const Player* player,int x,int y,int resource); +extern Unit* FindResource(const Unit* unit,int x,int y,int range); /// Find nearest deposit -extern Unit* FindDeposit(const Player*,int x,int y,int resource); +extern Unit* FindDeposit(const Unit* unit,int x,int y,int range); /// Find the next idle worker extern Unit* FindIdleWorker(const Player* player,const Unit* last); + /// Find the neareast piece of terrain with specific flags. +extern int FindTerrainType(int movemask,int resmask,int rvresult,int range, + const Player *player,int x,int y,int* px,int* py); /// Find the nearest piece of wood in sight range extern int FindWoodInSight(const Unit* unit,int* x,int* y); diff --git a/src/include/unittype.h b/src/include/unittype.h index eab368881..157221437 100644 --- a/src/include/unittype.h +++ b/src/include/unittype.h @@ -38,7 +38,7 @@ ----------------------------------------------------------------------------*/ /** -** @struct _unit_type_ unittype.h +** @struct _unit_type_ unittype.h ** ** \#include "unittype.h" ** @@ -328,54 +328,17 @@ ** ** Resource can be harvested. It's false for things like ** oil patches. +** FIXME: crappy name. ** ** UnitType::Harvester ** -** Unit is a resource worker. +** Unit is a resource worker. Faster than examining ResInfo ** -** UnitType::HarvestFromOutside ** -** Unit will harvest from the outside. The unit will use it's -** Attack animation (seems it turned into a generic Action anim.) +** UnitType::ResInfo[::MaxCosts] ** -** UnitType::ResourceHarvested -** -** The resource it can harvest. Needs Harvester flag. An unit -** can't harvest more than one type of resource. -** FIXME: implement something like TransformForOtherResource. -** -** UnitType::WaitAtResource -** -** Cycles the unit waits while inside a resource. -** -** UnitType::ResourceStep -** -** The unit makes so-caled mining cycles. Each mining cycle -** it does some sort of animation and gains ResourceStep -** resources. You can stop after any number of steps. -** when the quantity in the harvester reaches the maximum -** (ResourceCapacity) it will return home. I this is 0 then -** it's considered infinity, and ResourceCapacity will now -** be the limit. -** -** UnitType::ResourceCapacity -** -** Maximum amount of resources a harvester can carry. The -** actual amount can be modified while unloading. -** -** UnitType::WaitAtDepot -** -** Cycles the unit waits while inside the depot to unload. -** -** UnitType::TransformWhenEmpty; -** -** The harvester will transform into another unit when it is -** empty. FIXME: just change the animation. -** -** UnitType::TransformWhenLoaded -** -** The harvester will transform into another unit when it is -** loaded. FIXME: just change the animation. +** Information about resource harvesting. If NULL, it can't +** harvest it. ** ** UnitType::MustBuildOnTop ** @@ -520,6 +483,73 @@ ** ** Sprite images of the player colors. This image is drawn ** over UnitType::Sprite. Used with OpenGL only. +** +** +** +** @struct _resource_info_ unittype.h +** +** \#include "unittype.h" +** +** typedef struct _unit_type_ UnitType; +** +** This struct contains information about how a unit will harvest a +** resource. +** +** ResourceInfo::FileWhenLoaded +** +** The harvester's animation file will change when it's loaded. +** +** ResourceInfo::FileWhenEmpty; +** +** The harvester's animation file will change when it's empty. +** The standard animation is used only when building/repairing. +** +** +** ResourceInfo::HarvestFromOutside +** +** Unit will harvest from the outside. The unit will use it's +** Attack animation (seems it turned into a generic Action anim.) +** +** ResourceInfo::ResourceId +** +** The resource this is for. Mostly redundant. +** +** ResourceInfo::FinalResource +** +** The resource is converted to this at the depot. Usefull for +** a fisherman who harvests fish, but it all turns to food at the +** depot. +** +** ResourceInfo::WaitAtResource +** +** Cycles the unit waits while inside a resource. +** +** ResourceInfo::ResourceStep +** +** The unit makes so-caled mining cycles. Each mining cycle +** it does some sort of animation and gains ResourceStep +** resources. You can stop after any number of steps. +** when the quantity in the harvester reaches the maximum +** (ResourceCapacity) it will return home. I this is 0 then +** it's considered infinity, and ResourceCapacity will now +** be the limit. +** +** ResourceInfo::ResourceCapacity +** +** Maximum amount of resources a harvester can carry. The +** actual amount can be modified while unloading. +** +** ResourceInfo::WaitAtDepot +** +** Cycles the unit waits while inside the depot to unload. +** +** ResourceInfo::TerrainHarvester +** +** The unit will harvest terrain. For now this only works +** for wood. maybe it could be made to work for rocks, but +** more than that requires a tileset rewrite. +** FIXME: more configurable. +** */ /*---------------------------------------------------------------------------- @@ -584,6 +614,23 @@ typedef struct _missile_config_ { MissileType*Missile; /// Identifier to use to run time } MissileConfig; +typedef struct _resource_info_ { + char * FileWhenLoaded; /// Change the graphic when the unit is loaded. + char * FileWhenEmpty; /// Change the graphic when the unit is empty. + unsigned HarvestFromOutside; /// Unit harvests without entering the building. + unsigned WaitAtResource; /// Cycles the unit waits while mining. + unsigned ResourceStep; /// Resources the unit gains per mining cycle. + unsigned ResourceCapacity; /// Max amount of resources to carry. + unsigned WaitAtDepot; /// Cycles the unit waits while returning. + unsigned ResourceId; /// Id of the resource harvested. Redundant. + unsigned FinalResource; /// Convert resource when delivered. + unsigned TerrainHarvester; /// Unit will harvest terrain(wood only for now). + unsigned LoseResources; /// The unit will lose it's resource when distracted. + // Runtime info: + Graphic * SpriteWhenLoaded; /// The graphic corresponding to FileWhenLoaded. + Graphic * SpriteWhenEmpty; /// The graphic corresponding to FileWhenEmpty +} ResourceInfo; + /** ** Typedef of base structure of unit-type */ @@ -664,6 +711,7 @@ struct _unit_type_ { #define CanTargetSea 2 /// Can attack sea units #define CanTargetAir 4 /// Can attack air units + unsigned EquivType : 1; /// unsigned Revealer : 1; /// reveal the fog of war unsigned LandUnit : 1; /// Land animated unsigned AirUnit : 1; /// Air animated @@ -692,20 +740,13 @@ struct _unit_type_ { unsigned Hero : 1; /// Is hero only used for triggers . unsigned Volatile : 1; /// Invisiblity/unholy armor kills unit. unsigned Organic : 1; /// Organic can be healed. - + unsigned CanStore[MaxCosts]; /// Resources that we can store here. unsigned GivesResource; /// The resource this unit gives. unsigned MaxWorkers; /// Maximum number of workers. unsigned CanHarvest : 1; /// Resource can be harvested. - unsigned Harvester : 1; /// Unit is a resource worker. - unsigned HarvestFromOutside; /// Unit harvests without entering the building. - unsigned ResourceHarvested; /// The resource it can harvest. - unsigned WaitAtResource; /// Cycles the unit waits while mining. - unsigned ResourceStep; /// Resources the unit gains per mining cycle. - unsigned ResourceCapacity; /// Max amount of resources to carry. - unsigned WaitAtDepot; /// Cycles the unit waits while returning. - UnitType* TransformWhenEmpty; /// UnitType to transform to when empty. - UnitType* TransformWhenLoaded; /// UnitType to transform to when loaded. + unsigned Harvester : 1; /// unit is a resource harvester. + ResourceInfo* ResInfo[MaxCosts]; /// Resource information. UnitType* MustBuildOnTop; /// Must be built on top of something. unsigned SelectableByRectangle : 1; /// Selectable with mouse rectangle. @@ -756,10 +797,6 @@ extern UnitType* UnitTypes[UnitTypeMax]; /// All unit-types extern int NumUnitTypes; /// Number of unit-types made // FIXME: this hardcoded unit-types must be removed!! -extern UnitType*UnitTypeHumanWorker; /// Human worker -extern UnitType*UnitTypeOrcWorker; /// Orc worker -extern UnitType*UnitTypeHumanWorkerWithWood; /// Human worker with wood -extern UnitType*UnitTypeOrcWorkerWithWood; /// Orc worker with wood extern UnitType*UnitTypeHumanWall; /// Human wall extern UnitType*UnitTypeOrcWall; /// Orc wall extern UnitType*UnitTypeCritter; /// Critter unit-type pointer diff --git a/src/map/map.cpp b/src/map/map.cpp index 6e6bfb4f5..ff98a9f01 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -307,14 +307,14 @@ global int CheckedForestOnMap(int tx,int ty) */ global int ForestOnMap(int tx,int ty) { - IfDebug( - if( tx<0 || ty<0 || tx>=TheMap.Width || ty>=TheMap.Height ) { - // FIXME: must cleanup calling function ! - fprintf(stderr,"Used x %d, y %d\n",tx,ty); - abort(); - return 0; - } - ); +#ifdef DEBUG + if( tx<0 || ty<0 || tx>=TheMap.Width || ty>=TheMap.Height ) { + // FIXME: must cleanup calling function ! + fprintf(stderr,"Used x %d, y %d\n",tx,ty); + abort(); + return 0; + } +#endif return TheMap.Fields[tx+ty*TheMap.Width].Flags&MapFieldForest; } diff --git a/src/map/minimap.cpp b/src/map/minimap.cpp index d1ec7e5fd..9e0d796b1 100644 --- a/src/map/minimap.cpp +++ b/src/map/minimap.cpp @@ -301,7 +301,7 @@ global void UpdateMinimap(void) if( (*table)->Player->Player==PlayerNumNeutral ) { if( type->Critter ) { color=ColorNPC; - } else if( type->OilPatch ) { + } else if( type->GivesResource==OilCost ) { color=ColorBlack; } else { color=ColorYellow; diff --git a/src/network/commands.cpp b/src/network/commands.cpp index e57709679..7aab3503b 100644 --- a/src/network/commands.cpp +++ b/src/network/commands.cpp @@ -561,8 +561,8 @@ local void DoNextReplay(void) SendCommandBuildBuilding(UnitSlots[unit],posx,posy,UnitTypeByIdent(val),flags); } else if( !strcmp(name,"cancel-build") ) { SendCommandCancelBuilding(UnitSlots[unit],dunit); - } else if( !strcmp(name,"harvest") ) { - SendCommandHarvest(UnitSlots[unit],posx,posy,flags); + } else if( !strcmp(name,"resource-loc") ) { + SendCommandResourceLoc(UnitSlots[unit],posx,posy,flags); } else if( !strcmp(name,"resource") ) { SendCommandResource(UnitSlots[unit],dunit,flags); } else if( !strcmp(name,"return") ) { @@ -902,20 +902,20 @@ global void SendCommandCancelBuilding(Unit* unit,Unit* worker) } /** -** Send command: Unit harvest wood. +** Send command: Unit harvests a location (wood for now). ** ** @param unit pointer to unit. ** @param x X map tile position where to harvest. ** @param y Y map tile position where to harvest. ** @param flush Flag flush all pending commands. */ -global void SendCommandHarvest(Unit* unit,int x,int y,int flush) +global void SendCommandResourceLoc(Unit* unit,int x,int y,int flush) { if( NetworkFildes==-1 ) { - CommandLog("harvest",unit,flush,x,y,NoUnitP,NULL,-1); - CommandHarvest(unit,x,y,flush); + CommandLog("resource-loc",unit,flush,x,y,NoUnitP,NULL,-1); + CommandResourceLoc(unit,x,y,flush); } else { - NetworkSendCommand(MessageCommandHarvest,unit,x,y,NoUnitP,0,flush); + NetworkSendCommand(MessageCommandResourceLoc,unit,x,y,NoUnitP,0,flush); } } @@ -923,7 +923,7 @@ global void SendCommandHarvest(Unit* unit,int x,int y,int flush) ** Send command: Unit harvest resources ** ** @param unit pointer to unit. -** @param dest pointer to destination (oil-platform). +** @param dest pointer to destination (oil-platform,gold mine). ** @param flush Flag flush all pending commands. */ global void SendCommandResource(Unit* unit,Unit* dest,int flush) @@ -1308,9 +1308,9 @@ global void ParseCommand(unsigned char msgnr,UnitRef unum, CommandLog("cancel-build",unit,FlushCommands,-1,-1,dest,NULL,-1); CommandCancelBuilding(unit,dest); break; - case MessageCommandHarvest: - CommandLog("harvest",unit,status,x,y,NoUnitP,NULL,-1); - CommandHarvest(unit,x,y,status); + case MessageCommandResourceLoc: + CommandLog("resource-loc",unit,status,x,y,NoUnitP,NULL,-1); + CommandResourceLoc(unit,x,y,status); break; case MessageCommandResource: dest=NoUnitP; diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp index 98abced61..162bb3a05 100644 --- a/src/sound/sound.cpp +++ b/src/sound/sound.cpp @@ -181,9 +181,7 @@ local SoundId ChooseUnitVoiceSoundId(const Unit *unit,UnitVoiceGroup voice) return GameSounds.TreeChopping.Sound; case VoiceWorkCompleted: // FIXME: make this more configurable - if (unit->Type==UnitTypeHumanWorker) { - return GameSounds.PeasantWorkComplete.Sound; - } else if( ThisPlayer->Race==PlayerRaceHuman ) { + if( ThisPlayer->Race==PlayerRaceHuman ) { return GameSounds.HumanWorkComplete.Sound; } else { return GameSounds.OrcWorkComplete.Sound; diff --git a/src/stratagus/stratagus.cpp b/src/stratagus/stratagus.cpp index b9bcc05a9..dc6d53919 100644 --- a/src/stratagus/stratagus.cpp +++ b/src/stratagus/stratagus.cpp @@ -989,11 +989,7 @@ global void ShowLoadProgress(const char* fmt,...) char* s; va_start(va,fmt); -#ifdef USE_WIN32 - vsprintf(temp,fmt,va); -#else vsnprintf(temp,sizeof(temp),fmt,va); -#endif va_end(va); if( VideoDepth && IsFontLoaded(GameFont) ) { diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index 4e9292bbe..555a8b2b2 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -594,9 +594,7 @@ global void DrawButtonPanel(void) case ButtonHarvest: case ButtonReturn: for( j=0; j<NumSelected; ++j ) { - if( Selected[j]->Orders[0].Action!=UnitActionResource - && Selected[j]->Orders[0].Action - !=UnitActionHarvest ) { + if( Selected[j]->Orders[0].Action!=UnitActionResource ) { break; } } diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index ba8b8978a..a67a3a48f 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -1082,7 +1082,7 @@ global int HandleCheats(const char* Input) if( !ThisPlayer->Ai ) { AiInit( ThisPlayer ); } - SetMessage("I'm an AI Now :)"); + SetMessage("I'm the BORG, resistance is futile!"); } #endif } else { diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index bf747cd36..bc5a6fd1f 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -521,17 +521,22 @@ global void DrawUnitInfo(const Unit* unit) VideoDrawText(x+63,y+8+125,GameFont,"Speed:"); DrawStats(x+108,y+8+125,stats->Speed,type->_Speed); - // Show how much wood is harvested already in percents! :) //vladi - // FIXME: Make this optional - if( unit->Orders[0].Action==UnitActionHarvest && unit->SubAction==64 ) { - sprintf(buf,"Wood: %d%%" - ,(100*(CHOP_FOR_WOOD-unit->Value))/CHOP_FOR_WOOD); - VideoDrawText(x+63,y+8+141,GameFont,buf); - } // FIXME: Ugly hack. - if( unit->Type->Harvester&&unit->Value&&unit->Orders[0].Action!=UnitActionHarvest ) { + if( unit->Type->Harvester&&unit->Value) { sprintf(buf,"Carry: %d %s",unit->Value, - DefaultResourceNames[unit->Type->ResourceHarvested]); + DefaultResourceNames[unit->CurrentResource]); + VideoDrawText(x+61,y+8+141,GameFont,buf); + } + if ((unit->Type->Harvester)&& + (unit->Orders->Action==UnitActionResource)&& + (unit->CurrentResource)&& + (unit->Type->ResInfo[unit->CurrentResource]) && + (unit->SubAction==60) && + (!unit->Type->ResInfo[unit->CurrentResource]->ResourceStep)) { + sprintf(buf,"%s: %d%%",DefaultResourceNames[unit->CurrentResource], + 100*(unit->Type->ResInfo[unit->CurrentResource]->WaitAtResource- + unit->Data.ResWorker.TimeToHarvest)/ + unit->Type->ResInfo[unit->CurrentResource]->WaitAtResource); VideoDrawText(x+61,y+8+141,GameFont,buf); } diff --git a/src/ui/menu_proc.cpp b/src/ui/menu_proc.cpp index c410fcdf0..9c54783a6 100644 --- a/src/ui/menu_proc.cpp +++ b/src/ui/menu_proc.cpp @@ -40,12 +40,10 @@ #include "stratagus.h" -#if defined(USE_OPENGL) && defined(_MSC_VER) +#if defined(_MSC_VER) #undef NOUSER -#include "video.h" -#else -#include "video.h" #endif +#include "video.h" #include "font.h" #include "interface.h" #include "menus.h" diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp index 1fbe404e1..428b04e50 100644 --- a/src/ui/mouse.cpp +++ b/src/ui/mouse.cpp @@ -111,6 +111,7 @@ global void DoRightButton(int sx,int sy) int action; int acknowledged; int flush; + int res; // // No unit selected @@ -196,41 +197,45 @@ global void DoRightButton(int sx,int sy) // Handle resource workers. // if( action==MouseActionHarvest ) { - // Return wood cutter home - if( (type==UnitTypeOrcWorkerWithWood || - type==UnitTypeHumanWorkerWithWood) && - dest && dest->Type->CanStore[WoodCost] && - dest->Player==unit->Player) { - DebugLevel3("send to wood deposit.\n"); - dest->Blink=4; - SendCommandReturnGoods(unit,dest,flush); - continue; - } - // Send wood cutter to work - if((type==UnitTypeOrcWorker||type==UnitTypeHumanWorker) && - IsMapFieldExplored(unit->Player,x,y) && - ForestOnMap(x,y) ) { - SendCommandHarvest(unit,x,y,flush); - continue; - } - if (unit->Type->Harvester && dest) { - // Return a loaded harvester to deposit - if( unit->Value>0 && - dest->Type->CanStore[unit->Type->ResourceHarvested] && - dest->Player==unit->Player) { - dest->Blink=4; - DebugLevel3Fn("Return to deposit.\n"); - SendCommandReturnGoods(unit,dest,flush); - continue; - } - // Go and harvest - if( (unit->Value<unit->Type->ResourceCapacity) && - dest->Type->GivesResource==unit->Type->ResourceHarvested && - (dest->Player==unit->Player || - dest->Player->Player==PlayerMax-1)) { - dest->Blink=4; - SendCommandResource(unit,dest,flush); - continue; + if (unit->Type->Harvester) { + if ( dest ) { + // Return a loaded harvester to deposit + if( (unit->Value>0) && + (dest->Type->CanStore[unit->CurrentResource])&& + (dest->Player==unit->Player) ) { + dest->Blink=4; + DebugLevel3Fn("Return to deposit.\n"); + SendCommandReturnGoods(unit,dest,flush); + continue; + } + // Go and harvest from an building + if( (res=dest->Type->GivesResource) && + (unit->Type->ResInfo[res]) && + (unit->Value<unit->Type->ResInfo[res]->ResourceCapacity) && + (dest->Type->CanHarvest) && + (dest->Player==unit->Player || + (dest->Player->Player==PlayerMax-1)) ) { + dest->Blink=4; + SendCommandResource(unit,dest,flush); + continue; + } + } else { + // FIXME: support harvesting more types of terrain. + for (res=0;res<MaxCosts;res++) { + if ( (unit->Type->ResInfo[res]) && + (unit->Type->ResInfo[res]->TerrainHarvester) && + (IsMapFieldExplored(unit->Player,x,y)) && + (ForestOnMap(x,y)) && + ((unit->CurrentResource!=res)|| + (unit->Value<unit->Type->ResInfo[res]->ResourceCapacity))) { + DebugLevel3("Sent worker to cut wood.\n"); + SendCommandResourceLoc(unit,x,y,flush); + break; + } + } + if (res!=MaxCosts) { + continue; + } } } // Go and repair @@ -358,7 +363,7 @@ global void DoRightButton(int sx,int sy) } if( IsMapFieldExplored(unit->Player,x,y) && ForestOnMap(x,y) ) { DebugLevel3("RALY POINT TO FOREST\n"); - SendCommandHarvest(Selected[i],x,y,!(KeyModifiers&ModifierShift)); + SendCommandResourceLoc(Selected[i],x,y,!(KeyModifiers&ModifierShift)); continue; } SendCommandMove(unit,x,y,flush); @@ -945,23 +950,31 @@ local void SendDemolish(int sx,int sy) ** ** @see Selected */ -local void SendHarvest(int sx,int sy) +local void SendResource(int sx,int sy) { int i; + int res; Unit* dest; for( i=0; i<NumSelected; ++i ) { - DebugCheck(!Selected[i]->Type->Harvester); if ((dest=UnitUnderCursor) && - (Selected[i]->Type->ResourceHarvested==dest->Type->GivesResource)) { + (Selected[i]->Type->Harvester) && + (res=dest->Type->GivesResource) && + (Selected[i]->Type->ResInfo[res])) { dest->Blink=4; DebugLevel3("RESOURCE\n"); SendCommandResource(Selected[i],dest,!(KeyModifiers&ModifierShift)); continue; } - if( IsMapFieldExplored(Selected[i]->Player,sx/TileSizeX,sy/TileSizeY) && - ForestOnMap(sx/TileSizeX,sy/TileSizeY) ) { - SendCommandHarvest(Selected[i],sx/TileSizeY,sy/TileSizeY,!(KeyModifiers&ModifierShift)); + for (res=0;res<MaxCosts;res++) { + if( Selected[i]->Type->Harvester && + Selected[i]->Type->ResInfo[res] && + Selected[i]->Type->ResInfo[res]->TerrainHarvester && + IsMapFieldExplored(Selected[i]->Player,sx/TileSizeX,sy/TileSizeY) && + ForestOnMap(sx/TileSizeX,sy/TileSizeY) ) { + DebugLevel3("RESOURCE\n"); + SendCommandResourceLoc(Selected[i],sx/TileSizeY,sy/TileSizeY,!(KeyModifiers&ModifierShift)); + } } } } @@ -1070,7 +1083,7 @@ local void SendCommand(int sx, int sy) SendPatrol(sx,sy); break; case ButtonHarvest: - SendHarvest(sx,sy); + SendResource(sx,sy); break; case ButtonUnload: SendUnload(sx,sy); diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index aff70a46b..fb34b0cb2 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -58,6 +58,8 @@ /// Get unit-type. extern UnitType* CclGetUnitType(SCM ptr); + /// Get resource by name +extern unsigned CclGetResourceByName(SCM ptr); /** ** Set hit-point regeneration @@ -209,8 +211,6 @@ local void CclParseOrder(SCM list,Order* order) order->Action=UnitActionBuild; } else if( gh_eq_p(value,gh_symbol2scm("action-repair")) ) { order->Action=UnitActionRepair; - } else if( gh_eq_p(value,gh_symbol2scm("action-harvest")) ) { - order->Action=UnitActionHarvest; } else if( gh_eq_p(value,gh_symbol2scm("action-resource")) ) { order->Action=UnitActionResource; } else if( gh_eq_p(value,gh_symbol2scm("action-return-goods")) ) { @@ -366,6 +366,29 @@ local void CclParseBuilded(Unit* unit, SCM list) } } +/** +** Parse res worker data +** +** @param unit Unit pointer which should be filled with the data. +** @param list All options of the resource worker data. +*/ +local void CclParseResWorker(Unit* unit, SCM list) +{ + SCM value; + + while (!gh_null_p(list)) { + value = gh_car(list); + list = gh_cdr(list); + if (gh_eq_p(value, gh_symbol2scm("time-to-harvest"))) { + unit->Data.ResWorker.TimeToHarvest = gh_scm2int(gh_car(list)); + list = gh_cdr(list); + } else if (gh_eq_p(value, gh_symbol2scm("done-harvesting"))) { + unit->Data.ResWorker.DoneHarvesting = 1; + list = gh_cdr(list); + } + } +} + /** ** Parse research ** @@ -681,6 +704,9 @@ local SCM CclUnit(SCM list) } else if( gh_eq_p(value,gh_symbol2scm("value")) ) { unit->Value=gh_scm2int(gh_car(list)); list=gh_cdr(list); + } else if( gh_eq_p(value,gh_symbol2scm("current-resource")) ) { + unit->CurrentResource=CclGetResourceByName(gh_car(list)); + list=gh_cdr(list); } else if( gh_eq_p(value,gh_symbol2scm("sub-action")) ) { unit->SubAction=gh_scm2int(gh_car(list)); list=gh_cdr(list); @@ -761,6 +787,10 @@ local SCM CclUnit(SCM list) sublist=gh_car(list); list=gh_cdr(list); CclParseBuilded(unit,sublist); + } else if( gh_eq_p(value,gh_symbol2scm("data-res-worker")) ) { + sublist=gh_car(list); + list=gh_cdr(list); + CclParseResWorker(unit,sublist); } else if( gh_eq_p(value,gh_symbol2scm("data-research")) ) { sublist=gh_car(list); list=gh_cdr(list); diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp index d5cec1b6d..da3b94d4e 100644 --- a/src/unit/script_unittype.cpp +++ b/src/unit/script_unittype.cpp @@ -71,7 +71,7 @@ local ccl_smob_type_t SiodUnitTypeTag; /// siod unit-type object ** @param value SCM thingie ** @return the resource id */ -local unsigned CclGetResourceByName(SCM value) +global unsigned CclGetResourceByName(SCM value) { int i; for( i=0; i<MaxCosts; ++i ) { @@ -96,6 +96,7 @@ local SCM CclDefineUnitType(SCM list) SCM sublist; UnitType* type; UnitType* auxtype; + ResourceInfo* res; char* str; int i; int redefine; @@ -271,7 +272,7 @@ local SCM CclDefineUnitType(SCM list) auxtype=UnitTypeByIdent(str); if (!auxtype) { DebugLevel0("Build on top of undefined unit \"%s\".\n" _C_ str); - exit(0); + DebugCheck(1); } type->MustBuildOnTop=auxtype; free(str); @@ -428,43 +429,55 @@ local SCM CclDefineUnitType(SCM list) type->Coward=1; } else if( gh_eq_p(value,gh_symbol2scm("harvester")) ) { type->Harvester=1; - } else if( gh_eq_p(value,gh_symbol2scm("harvest-from-outside")) ) { - type->HarvestFromOutside=1; - } else if( gh_eq_p(value,gh_symbol2scm("resource-step")) ) { - type->ResourceStep=gh_scm2int(gh_car(list)); + } else if( gh_eq_p(value,gh_symbol2scm("can-gather-resource")) ) { + sublist=gh_car(list); list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("resource-harvested")) ) { - type->ResourceHarvested=CclGetResourceByName(gh_car(list)); - list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("wait-at-resource")) ) { - type->WaitAtResource=gh_scm2int(gh_car(list)); - list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("wait-at-depot")) ) { - type->WaitAtDepot=gh_scm2int(gh_car(list)); - list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("resource-capacity")) ) { - type->ResourceCapacity=gh_scm2int(gh_car(list)); - list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("transform-when-empty")) ) { - str=gh_scm2newstr(gh_car(list),NULL); - auxtype=UnitTypeByIdent(str); - if (!auxtype) { - DebugLevel0("Undefined unit \"%s\".\n" _C_ str); - exit(0); + res=(ResourceInfo*)malloc(sizeof(ResourceInfo)); + memset(res,0,sizeof(ResourceInfo)); + while( !gh_null_p(sublist) ) { + value=gh_car(sublist); + sublist=gh_cdr(sublist); + if (gh_eq_p(value,gh_symbol2scm("resource-id")) ) { + res->ResourceId=CclGetResourceByName(gh_car(sublist)); + type->ResInfo[res->ResourceId]=res; + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("resource-step")) ) { + res->ResourceStep=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("final-resource")) ) { + res->FinalResource=CclGetResourceByName(gh_car(sublist)); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("wait-at-resource")) ) { + res->WaitAtResource=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("wait-at-depot")) ) { + res->WaitAtDepot=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("resource-capacity")) ) { + res->ResourceCapacity=gh_scm2int(gh_car(sublist)); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("terrain-harvester")) ) { + res->TerrainHarvester=1; + } else if( gh_eq_p(value,gh_symbol2scm("lose-resources")) ) { + res->LoseResources=1; + } else if( gh_eq_p(value,gh_symbol2scm("harvest-from-outside")) ) { + res->HarvestFromOutside=1; + } else if( gh_eq_p(value,gh_symbol2scm("file-when-empty")) ) { + res->FileWhenLoaded=gh_scm2newstr(gh_car(sublist),0); + sublist=gh_cdr(sublist); + } else if( gh_eq_p(value,gh_symbol2scm("file-when-loaded")) ) { + res->FileWhenLoaded=gh_scm2newstr(gh_car(sublist),0); + sublist=gh_cdr(sublist); + } else { + printf("\n%s\n",type->Name); + errl("Unsupported tag",value); + DebugCheck( 1 ); + } } - type->TransformWhenEmpty=auxtype; - free(str); - list=gh_cdr(list); - } else if( gh_eq_p(value,gh_symbol2scm("transform-when-loaded")) ) { - str=gh_scm2newstr(gh_car(list),NULL); - auxtype=UnitTypeByIdent(str); - if (!auxtype) { - DebugLevel0("Undefined unit \"%s\".\n" _C_ str); - exit(0); + if (!res->FinalResource) { + res->FinalResource=res->ResourceId; } - type->TransformWhenLoaded=auxtype; - free(str); - list=gh_cdr(list); + DebugCheck(!res->ResourceId); } else if( gh_eq_p(value,gh_symbol2scm("gives-resource")) ) { type->GivesResource=CclGetResourceByName(gh_car(list)); list=gh_cdr(list); @@ -562,8 +575,8 @@ local SCM CclDefineUnitType(SCM list) } else { // FIXME: this leaves a half initialized unit-type printf("\n%s\n",type->Name); - DebugCheck( 1 ); errl("Unsupported tag",value); + DebugCheck( 1 ); } } diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index f62940621..17fc1d14a 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -2462,15 +2462,152 @@ global int CanBuildUnitType(const Unit* unit,const UnitType* type,int x,int y) ----------------------------------------------------------------------------*/ /** - ** Find wood in sight range. + ** Find the closest piece of wood for an unit. ** - ** @param unit Unit that needs wood. - ** @param px OUT: Map X position of wood. - ** @param py OUT: Map Y position of wood. + ** @param unit The unit. + ** @param x OUT: Map X position of tile. + ** @param y OUT: Map Y position of tile. + */ +global int FindWoodInSight(const Unit* unit,int* x,int* y) +{ + return FindTerrainType(UnitMovementMask(unit),0,MapFieldForest,9999, + unit->Player,unit->X,unit->Y,x,y); +} + +/** + ** Find the closest piece of terrain with the given flags. + ** + ** @param movemask The movement mask to reach that location. + ** @param resmask Result tile mask. + ** @param rvresult Return a tile that doesn't match. + ** @param range Maximum distance for the search. + ** @param player Only search fields explored by player + ** @param x Map X start position for the search. + ** @param y Map Y start position for the search. + ** + ** @param px OUT: Map X position of tile. + ** @param py OUT: Map Y position of tile. + ** + ** @notes Movement mask can be 0xFFFFFFFF to have no effect + ** Range is not circular, but square. + ** Player is ignored if nil(search the entire map) + ** Use rvresult if you search for a til;e that doesn't + ** match resmask. Like for a tile where an unit can go + ** with it's movement mask. ** ** @return True if wood was found. */ -global int FindWoodInSight(const Unit* unit,int* px,int* py) +global int FindTerrainType(int movemask,int resmask,int rvresult,int range, + const Player *player,int x,int y,int* px,int* py) +{ + static const int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 }; + static const int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 }; + struct { + unsigned short X; + unsigned short Y; + } * points; + int size; + int rx; + int ry; + int wp; + int rp; + int ep; + int i; + int w; + unsigned char* m; + unsigned char* matrix; + int destx; + int desty; + int cdist; + + destx=x; + desty=y; + size=min(TheMap.Width*TheMap.Height/4,range*range*5); + points=malloc(size*sizeof(*points)); + + // Make movement matrix. FIXME: can create smaller matrix. + matrix=CreateMatrix(); + w=TheMap.Width+2; + matrix+=w+w+2; + points[0].X=x; + points[0].Y=y; + rp=0; + matrix[x+y*w]=1; // mark start point + ep=wp=1; // start with one point + cdist=0; // current distance is 0 + + // + // Pop a point from stack, push all neighbors which could be entered. + // + for( ;; ) { + while( rp!=ep ) { + rx=points[rp].X; + ry=points[rp].Y; + DebugLevel3("%d,%d\n" _C_ rx _C_ ry); + for( i=0; i<8; ++i ) { // mark all neighbors + x=rx+xoffset[i]; + y=ry+yoffset[i]; + // Make sure we don't leave the map. + if (x<0||y<0||x>=TheMap.Width||y>=TheMap.Height) { + continue; + } + m=matrix+x+y*w; + // Check if visited or unexplored + if( *m || (player&&!IsMapFieldExplored(player,x,y))) { + continue; + } + // Look if found what was required. + if ( rvresult?CanMoveToMask(x,y,resmask):!CanMoveToMask(x,y,resmask) ) { + *px=x; + *py=y; + DebugLevel3("Found it! %X %X\n" _C_ TheMap.Fields[x+y*TheMap.Width].Flags _C_ resmask); + return 1; + } + if( CanMoveToMask(x,y,movemask) ) { // reachable + *m=1; + points[wp].X=x; // push the point + points[wp].Y=y; + if( ++wp>=size ) { // round about + wp=0; + } + if (wp==ep) { + // We are out of points, give up! + DebugLevel0Fn("Ran out of points the hard way, beware.\n"); + break; + } + } else { // unreachable + *m=99; + } + } + if( ++rp>=size ) { // round about + rp=0; + } + } + cdist++; + if( rp==wp||cdist>=range ) { // unreachable, no more points available + break; + } + // Continue with next set. + ep=wp; + } + free(points); + return 0; +} + +/** + ** Find Resource. + ** + ** @param unit The unit that wants to find a resource. + ** @param x Closest to x + ** @param x Closest to y + ** @param range Maximum distance to the resource. + ** + ** @notes This will return an usable resource building that + ** belongs to "player" or is neutral. + ** + ** @return NoUnitP or resource unit + */ +global Unit* FindResource(const Unit * unit,int x,int y,int range) { static const int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 }; static const int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 }; @@ -2479,8 +2616,6 @@ global int FindWoodInSight(const Unit* unit,int* px,int* py) unsigned short Y; } * points; int size; - int x; - int y; int rx; int ry; int mask; @@ -2493,80 +2628,42 @@ global int FindWoodInSight(const Unit* unit,int* px,int* py) unsigned char* m; unsigned char* matrix; const Unit* destu; + Unit* mine; + Unit* bestmine; int destx; int desty; - int bestx; - int besty; int bestd; + int cdist; + int resource; - destx=x=unit->X; - desty=y=unit->Y; - size=TheMap.Width*TheMap.Height/4; + resource=unit->CurrentResource; + + destx=x; + desty=y; + size=min(TheMap.Width*TheMap.Height/4,range*range*5); points=malloc(size*sizeof(*points)); - // - // Find the nearest wood depot - // - if( (destu=FindDeposit(unit->Player,x,y,WoodCost)) ) { + // Find the nearest gold depot + if( (destu=FindDeposit(unit,x,y,range)) ) { NearestOfUnit(destu,x,y,&destx,&desty); } bestd=99999; - IfDebug( bestx=besty=0; ); // keep the compiler happy - - // // Make movement matrix. FIXME: can create smaller matrix. - // matrix=CreateMatrix(); w=TheMap.Width+2; matrix+=w+w+2; - - // - // Mark sight range as border. FIXME: matrix didn't need to be bigger. - // - n=unit->Stats->SightRange; - rx=x-n; - if( rx<0 ) { - rx=0; - } - ep=x+n; - if( ep>TheMap.Width ) { - ep=TheMap.Width; - } - ry=y-n; - if( ry<0 ) { - ry=0; - } - wp=y+n; - if( wp>TheMap.Height ) { - wp=TheMap.Height; - } - for( i=rx; i<ep; ++i ) { // top bottom line - matrix[i+ry*w]=matrix[i+wp*w]=66; - } - for( i=ry+1; i<wp-1; ++i ) { - matrix[rx+i*w]=matrix[ep+i*w]=66; - } - -#if 0 - matrix[x+n+(y+n)*w]=matrix[x-n+(y+n)*w]= - matrix[x+n+(y-n)*w]=matrix[x-n+(y-n)*w]=66; - for( i=n; i--; ) { - // FIXME: marks out of map area - DebugCheck( x-i+(y-n)*w<0 || x+i+(y+n)*w>w*TheMap.Hight ); - matrix[x+n+(y+i)*w]=matrix[x-n+(y+i)*w]= - matrix[x+n+(y-i)*w]=matrix[x-n+(y-i)*w]= - matrix[x-i+(y+n)*w]=matrix[x+i+(y+n)*w]= - matrix[x-i+(y-n)*w]=matrix[x+i+(y-n)*w]=66; - } -#endif - + // Unit movement mask mask=UnitMovementMask(unit); - + // Ignore all units along the way. Might seem wierd, but otherwise + // peasants would lock at a mine with a lot of workers. + mask&=~(MapFieldLandUnit|MapFieldSeaUnit|MapFieldAirUnit); points[0].X=x; points[0].Y=y; rp=0; matrix[x+y*w]=1; // mark start point ep=wp=1; // start with one point + cdist=0; // current distance is 0 + bestmine=NoUnitP; // // Pop a point from stack, push all neighbors which could be entered. @@ -2583,23 +2680,28 @@ global int FindWoodInSight(const Unit* unit,int* px,int* py) continue; } + if (!IsMapFieldExplored(unit->Player,x,y)) { // Unknown. + continue; + } + // - // Look if there is wood + // Look if there is a mine // - if ( ForestOnMap(x,y) && IsMapFieldExplored(unit->Player,x,y) ) { + if ((mine=ResourceOnMap(x,y,resource))&& + (mine->Type->CanHarvest)&& + ((mine->Player->Player==PlayerMax-1)|| + (mine->Player==unit->Player)|| + (IsAllied(unit->Player,mine)))) { if( destu ) { n=max(abs(destx-x),abs(desty-y)); if( n<bestd ) { bestd=n; - bestx=x; - besty=y; + bestmine=mine; } - *m=22; + *m=99; } else { // no goal take the first - *px=x; - *py=y; free(points); - return 1; + return mine; } } @@ -2610,6 +2712,10 @@ global int FindWoodInSight(const Unit* unit,int* px,int* py) if( ++wp>=size ) { // round about wp=0; } + if (wp==ep) { + // We are out of points, give up! + break; + } } else { // unreachable *m=99; } @@ -2618,138 +2724,141 @@ global int FindWoodInSight(const Unit* unit,int* px,int* py) rp=0; } } - - // // Take best of this frame, if any. - // if( bestd!=99999 ) { - *px=bestx; - *py=besty; free(points); - return 1; + return bestmine; } - - // - // Continue with next frame. - // - if( rp==wp ) { // unreachable, no more points available + cdist++; + if( rp==wp||cdist>=range ) { // unreachable, no more points available break; } + // Continue with next set. ep=wp; } - - DebugLevel3Fn("no wood in sight-range\n"); - + DebugLevel3Fn("no resource found\n"); free(points); - return 0; -} - -/** - ** Find Resource. - ** - ** @param player The player that wants to find a resource. - ** @param x Nearest to X position. - ** @param y Nearest to Y position - ** @param resource The ID of the resource. - ** - ** @notes This will return an usable resource building that - ** belongs to "player" or is neutral. - ** - ** @return NoUnitP or oil platform unit - */ -global Unit* FindResource(const Player* player,int x,int y,int resource) -{ - Unit* unit; - Unit* best; - Unit** units; - int nunits; - int best_d; - int d; - int i; - int pnum; - - // FIXME: this is not the best one - // We need the deposit with the shortest way! - // At least it must be reachable! - - best=NoUnitP; - best_d=99999; - for (pnum=0;pnum<PlayerMax;++pnum) { - // FIXME: allow harvesting from ally - if ( (pnum!=PlayerMax-1) && (pnum!=player->Player) ) { - continue; - } - nunits=Players[pnum].TotalNumUnits; - units=Players[pnum].Units; - for( i=0; i<nunits; i++ ) { - unit=units[i]; - if( UnitUnusable(unit) || !unit->Type->CanHarvest ) { - continue; - } - // Want platform - if( unit->Type->GivesResource==resource ) { - d=MapDistanceToUnit(x,y,unit); - if( d<best_d ) { - best_d=d; - best=unit; - } - } - } - } - - DebugLevel3Fn("%d %d,%d\n" _C_ best?UnitNumber(best):-1 _C_ - best?best->X:-1 _C_ best?best->Y:-1); - return best; + return NoUnitP; } /** ** Find deposit. This will find a deposit for a resource ** - ** @param player The player the deposit must belong to. - ** @param x Nearest to X position. - ** @param y Nearest to Y position. - ** @param resource The resource you need the deposit to hold. + ** @param unit The unit that wants to find a resource. + ** @param x Closest to x + ** @param x Closest to y + ** @param range Maximum distance to the deposit. + ** + ** @notes This will return a reachable allied depot. ** ** @return NoUnitP or oil deposit unit */ -global Unit* FindDeposit(const Player* player,int x,int y,int resource) +global Unit* FindDeposit(const Unit* unit,int x,int y,int range) { - Unit* unit; - Unit* best; - Unit** units; - int nunits; - int best_d; - int d,i; + static const int xoffset[]={ 0,-1,+1, 0, -1,+1,-1,+1 }; + static const int yoffset[]={ -1, 0, 0,+1, -1,-1,+1,+1 }; + struct { + unsigned short X; + unsigned short Y; + } * points; + int size; + int rx; + int ry; + int mask; + int wp; + int rp; + int ep; + int i; + int w; + unsigned char* m; + unsigned char* matrix; + Unit* depot; + int destx; + int desty; + int cdist; + int resource; + + resource=unit->CurrentResource; + + destx=x; + desty=y; + size=min(TheMap.Width*TheMap.Height/4,range*range*5); + points=malloc(size*sizeof(*points)); + + // Make movement matrix. FIXME: can create smaller matrix. + matrix=CreateMatrix(); + w=TheMap.Width+2; + matrix+=w+w+2; + // Unit movement mask + mask=UnitMovementMask(unit); + // Ignore all units along the way. Might seem wierd, but otherwise + // peasants would lock at a mine with a lot of workers. + mask&=~(MapFieldLandUnit|MapFieldSeaUnit|MapFieldAirUnit); + points[0].X=x; + points[0].Y=y; + rp=0; + matrix[x+y*w]=1; // mark start point + ep=wp=1; // start with one point + cdist=0; // current distance is 0 - // FIXME: this is not the best one - // We need the deposit with the shortest way! - // At least it must be reachable! - // FIXME: Could we use unit-cache to find it faster? // - - best=NoUnitP; - best_d=INT_MAX; - nunits=player->TotalNumUnits; - units=player->Units; - for( i=0; i<nunits; i++ ) { - unit=units[i]; - if( UnitUnusable(unit) ) { - continue; - } - if( unit->Type->CanStore[resource] ) { - d=MapDistanceToUnit(x,y,unit); - if( d<best_d - // FIXME: UnitReachable didn't work with unit inside - /*&& (d=UnitReachable(source,unit,1)) && d<best_d*/ ) { - best_d=d; - best=unit; + // Pop a point from stack, push all neighbors which could be entered. + // + for( ;; ) { + while( rp!=ep ) { + rx=points[rp].X; + ry=points[rp].Y; + for( i=0; i<8; ++i ) { // mark all neighbors + x=rx+xoffset[i]; + y=ry+yoffset[i]; + // Make sure we don't leave the map. + if (x<0||y<0||x>=TheMap.Width||y>=TheMap.Height) { + continue; + } + m=matrix+x+y*w; + // Check if visited or unexplored + if( *m || !IsMapFieldExplored(unit->Player,x,y)) { + continue; + } + // + // Look if there is a mine + // + if ((depot=ResourceDepositOnMap(x,y,resource))&& + ((IsAllied(unit->Player,depot)) || + (unit->Player==depot->Player))) { + free(points); + return depot; + } + if( CanMoveToMask(x,y,mask) ) { // reachable + *m=1; + points[wp].X=x; // push the point + points[wp].Y=y; + if( ++wp>=size ) { // round about + wp=0; + } + if (wp==ep) { + // We are out of points, give up! + DebugLevel0Fn("Ran out of points the hard way, beware.\n"); + break; + } + } else { // unreachable + *m=99; + } + } + if( ++rp>=size ) { // round about + rp=0; } } + cdist++; + if( rp==wp||cdist>=range ) { // unreachable, no more points available + break; + } + // Continue with next set. + ep=wp; } - - DebugLevel3Fn("%d %d,%d\n" _C_ best?UnitNumber(best):-1 _C_ - best?best->X:-1 _C_ best?best->Y:-1); - return best; + DebugLevel3Fn("no resource deposit found\n"); + free(points); + return NoUnitP; } /** @@ -3146,14 +3255,14 @@ global void HitUnit(Unit* attacker,Unit* target,int damage) } #endif + // FIXME: this is dumb. I made repairers capture. crap. // david: capture enemy buildings // Only worker types can capture. // Still possible to destroy building if not careful (too many attackers) if( EnableBuildingCapture && attacker && type->Building && target->HP<=damage*3 && IsEnemy(attacker->Player,target) - && (attacker->Type==UnitTypeOrcWorker - || attacker->Type==UnitTypeHumanWorker) ) { + && attacker->Type->RepairRange ) { ChangeUnitOwner(target,attacker->Player); CommandStopUnit(attacker); // Attacker shouldn't continue attack! } @@ -3558,9 +3667,6 @@ local void SaveOrder(const Order* order,CLFile* file) case UnitActionRepair: CLprintf(file,"action-repair"); break; - case UnitActionHarvest: - CLprintf(file,"action-harvest"); - break; case UnitActionResource: CLprintf(file,"action-resource"); break; @@ -3731,6 +3837,9 @@ global void SaveUnit(const Unit* unit,CLFile* file) CLprintf(file,"'last-group %d\n ",unit->LastGroup); CLprintf(file,"'value %d\n ",unit->Value); + if (unit->CurrentResource) { + CLprintf(file,"'current-resource '%s\n ",DefaultResourceNames[unit->CurrentResource]); + } CLprintf(file,"'sub-action %d ",unit->SubAction); CLprintf(file,"'wait %d ",unit->Wait); @@ -3775,6 +3884,13 @@ global void SaveUnit(const Unit* unit,CLFile* file) CLprintf(file," 'resource-active %d",unit->Data.Resource.Active); } break; + case UnitActionResource: + CLprintf(file," 'data-res-worker '(time-to-harvest %d",unit->Data.ResWorker.TimeToHarvest); + if (unit->Data.ResWorker.DoneHarvesting) { + CLprintf(file," done-harvesting"); + } + CLprintf(file,")"); + break; case UnitActionBuilded: { ConstructionFrame* cframe; diff --git a/src/unit/unit_draw.cpp b/src/unit/unit_draw.cpp index db09912a9..c4627dcf9 100644 --- a/src/unit/unit_draw.cpp +++ b/src/unit/unit_draw.cpp @@ -965,13 +965,12 @@ local void DrawDecoration(const Unit* unit,const UnitType* type,int x,int y) ,unit->Data.UpgradeTo.Ticks); // - // Chopping wood. + // Carry resource. + // Don't display if empty. // - } else if( unit->Orders[0].Action==UnitActionHarvest - && unit->SubAction==64 ) { - DrawManaBar(x,y,type,CHOP_FOR_WOOD, - unit->Value>CHOP_FOR_WOOD? - 0:CHOP_FOR_WOOD-unit->Value); + } else if( unit->Type->Harvester&&unit->CurrentResource&&unit->Value>0 ) { + DrawManaBar(x,y,type,unit->Type->ResInfo[unit->CurrentResource]->ResourceCapacity, + unit->Value); // // Building research new technologie. @@ -1031,15 +1030,13 @@ local void DrawDecoration(const Unit* unit,const UnitType* type,int x,int y) DrawManaSprite(x,y,type,unit->Orders[0].Type ->Stats[unit->Player->Player].Costs[TimeCost] ,unit->Data.UpgradeTo.Ticks); - + // - // Chopping wood. + // Carry resource. // - } else if( unit->Orders[0].Action==UnitActionHarvest - && unit->SubAction==64 ) { - DrawManaSprite(x,y,type,CHOP_FOR_WOOD, - unit->Value>CHOP_FOR_WOOD? - 0:CHOP_FOR_WOOD-unit->Value); + } else if( unit->Type->Harvester&&unit->CurrentResource&&unit->Value>0 ) { + DrawManaSprite(x,y,type,unit->Type->ResInfo[unit->CurrentResource]->ResourceCapacity, + unit->Value); // // Building research new technologie. @@ -1492,11 +1489,6 @@ local void ShowSingleOrder(const Unit* unit, int x1, int y1, const Order* order) e_color = color = ColorGray; break; - case UnitActionHarvest: - e_color = color = ColorYellow; - dest = 1; - break; - case UnitActionResource: e_color = color = ColorYellow; dest = 1; @@ -1839,6 +1831,8 @@ global void DrawUnit(const Unit* unit) { int x; int y; + Graphic* Sprite; + ResourceInfo* resinfo; const UnitType* type; if ( unit->Type->Revealer ) { // Revealers are not drawn @@ -1859,13 +1853,35 @@ global void DrawUnit(const Unit* unit) DrawUnitSelection(unit); GraphicUnitPixels(unit,type->Sprite); - DrawUnitType(type,unit->Frame,x,y); + + Sprite=type->Sprite; + if (type->Harvester && unit->CurrentResource) { + resinfo=type->ResInfo[unit->CurrentResource]; + if (unit->Value) { + if (resinfo->SpriteWhenLoaded) { + Sprite=resinfo->SpriteWhenLoaded; + } + } else { + if (resinfo->SpriteWhenEmpty) { + Sprite=resinfo->SpriteWhenEmpty; + } + } + } + if( unit->Frame<0 ) { + VideoDrawClipX(Sprite,-unit->Frame, + x-(type->Width-type->TileWidth*TileSizeX)/2, + y-(type->Height-type->TileHeight*TileSizeY)/2); + } else { + VideoDrawClip(Sprite,unit->Frame, + x-(type->Width-type->TileWidth*TileSizeX)/2, + y-(type->Height-type->TileHeight*TileSizeY)/2); + } #ifdef USE_OPENGL DrawUnitPlayerColor(type,unit->Player->Player,unit->Frame,x,y); #endif #ifndef NEW_DECODRAW -// Unit's extras not fully supported.. need to be deocrations themselves. +// Unit's extras not fully supported.. need to be decorations themselves. DrawInformations(unit,type,x,y); #endif } diff --git a/src/unit/unit_find.cpp b/src/unit/unit_find.cpp index ca4d344d5..906860497 100644 --- a/src/unit/unit_find.cpp +++ b/src/unit/unit_find.cpp @@ -417,7 +417,7 @@ global Unit* ResourceOnMap(int tx,int ty,int resource) n=SelectUnitsOnTile(tx,ty,table); for( i=0; i<n; ++i ) { - if( UnitUnusable(table[i]) || !table[i]->Type->CanHarvest ){ + if( UnitUnusable(table[i]) || !table[i]->Type->CanHarvest || table[i]->Value==0) { continue; } if( table[i]->Type->GivesResource==resource ) { diff --git a/src/unit/unittype.cpp b/src/unit/unittype.cpp index 43fe4dfad..920fa03a3 100644 --- a/src/unit/unittype.cpp +++ b/src/unit/unittype.cpp @@ -76,10 +76,6 @@ global int NumUnitTypes; /// number of unit-types made ** ** FIXME: find a way to make it configurable! */ -global UnitType*UnitTypeHumanWorker; /// Human worker -global UnitType*UnitTypeOrcWorker; /// Orc worker -global UnitType*UnitTypeHumanWorkerWithWood; /// Human worker with wood -global UnitType*UnitTypeOrcWorkerWithWood; /// Orc worker with wood global UnitType*UnitTypeHumanWall; /// Human wall global UnitType*UnitTypeOrcWall; /// Orc wall global UnitType*UnitTypeCritter; /// Critter unit type pointer @@ -453,8 +449,13 @@ global void ParsePudUDTA(const char* udta,int length __attribute__((unused))) // Cowards unittype->Coward=BIT(8,v)|BIT(26,v); if (BIT(9,v)) { - unittype->Harvester=1; - unittype->ResourceHarvested=OilCost; + unittype->ResInfo[OilCost]=(ResourceInfo*)malloc(sizeof(ResourceInfo)); + memset(unittype->ResInfo[OilCost],0,sizeof(ResourceInfo)); + unittype->ResInfo[OilCost]->ResourceId=OilCost; + unittype->ResInfo[OilCost]->FinalResource=OilCost; + unittype->ResInfo[OilCost]->WaitAtResource=150; + unittype->ResInfo[OilCost]->WaitAtDepot=150; + unittype->ResInfo[OilCost]->ResourceCapacity=100; } unittype->Transporter=BIT(10,v); unittype->CanStore[GoldCost]=BIT(12,v); @@ -645,6 +646,7 @@ local void SaveUnitType(CLFile* file,const UnitType* type,int all) { int i; int flag; + ResourceInfo* res; CLprintf(file,"(define-unit-type '%s",type->Ident); CLprintf(file," 'name \"%s\"\n ",type->Name); @@ -905,36 +907,53 @@ local void SaveUnitType(CLFile* file,const UnitType* type,int all) if( type->Coward ) { CLprintf(file," 'coward\n"); } + if( type->Harvester ) { - CLprintf(file," 'harvester 'resource-harvested '%s\n",DefaultResourceNames[type->ResourceHarvested]); - } - if( type->HarvestFromOutside ) { - CLprintf(file," 'harvest-from-outside\n"); - } - if ( type->WaitAtResource ) { - CLprintf(file," 'wait-at-resource %d\n",type->WaitAtResource); - } - if ( type->WaitAtDepot ) { - CLprintf(file," 'wait-at-depot %d\n",type->WaitAtDepot); - } - if ( type->ResourceCapacity ) { - CLprintf(file," 'resource-capacity %d\n",type->ResourceCapacity); - } - if ( type->TransformWhenEmpty ) { - CLprintf(file," 'transform-when-empty '%s\n",type->TransformWhenEmpty->Ident); - } - if ( type->TransformWhenLoaded ) { - CLprintf(file," 'transform-when-loaded '%s\n",type->TransformWhenLoaded->Ident); + CLprintf(file," 'harvester\n"); + for (i=0;i<MaxCosts;i++) { + if (type->ResInfo[i]) { + res=type->ResInfo[i]; + CLprintf(file," 'can-gather-resource '(\n"); + CLprintf(file," resource-id %s\n",DefaultResourceNames[res->ResourceId]); + CLprintf(file," final-resource %s\n",DefaultResourceNames[res->FinalResource]); + if( res->HarvestFromOutside ) { + CLprintf(file," harvest-from-outside\n"); + } + if ( res->WaitAtResource ) { + CLprintf(file," wait-at-resource %d\n",res->WaitAtResource); + } + if ( res->WaitAtDepot ) { + CLprintf(file," wait-at-depot %d\n",res->WaitAtDepot); + } + if ( res->ResourceCapacity ) { + CLprintf(file," resource-capacity %d\n",res->ResourceCapacity); + } + if( res->ResourceStep ) { + CLprintf(file," resource-step %d\n",res->ResourceStep); + } + if( res->TerrainHarvester ) { + CLprintf(file," terrain-harvester\n"); + } + if( res->LoseResources ) { + CLprintf(file," lose-resources\n"); + } + if ( res->FileWhenEmpty ) { + CLprintf(file," file-when-empty %s\n",res->FileWhenEmpty); + } + if ( res->FileWhenLoaded ) { + CLprintf(file," file-when-loaded %s\n",res->FileWhenLoaded); + } + CLprintf(file," )\n"); + } + } } + if( type->GivesResource ) { CLprintf(file," 'gives-resource '%s\n",DefaultResourceNames[type->GivesResource]); } if( type->MaxWorkers ) { CLprintf(file," 'max-workers %d\n",type->MaxWorkers); } - if( type->ResourceStep ){ - CLprintf(file," 'resource-step %d\n",type->ResourceStep); - } // Save store info. for (flag=i=0;i<MaxCosts;i++) @@ -1222,10 +1241,6 @@ global void InitUnitTypes(int reset_player_stats) // // Setup hardcoded unit types. FIXME: should be moved to some configs. // - UnitTypeHumanWorker=UnitTypeByIdent("unit-peasant"); - UnitTypeOrcWorker=UnitTypeByIdent("unit-peon"); - UnitTypeHumanWorkerWithWood=UnitTypeByIdent("unit-peasant-with-wood"); - UnitTypeOrcWorkerWithWood=UnitTypeByIdent("unit-peon-with-wood"); UnitTypeHumanWall=UnitTypeByIdent("unit-human-wall"); UnitTypeOrcWall=UnitTypeByIdent("unit-orc-wall"); UnitTypeCritter=UnitTypeByIdent("unit-critter"); @@ -1239,18 +1254,37 @@ global void LoadUnitTypes(void) { UnitType* type; const char* file; + char buf[1000]; int i; + int res; + ResourceInfo* resinfo; for( i=0; i<NumUnitTypes; ++i ) { type=UnitTypes[i]; if( (file=type->ShadowFile) ) { - char *buf; - buf=alloca(strlen(file)+9+1); file=strcat(strcpy(buf,"graphics/"),file); ShowLoadProgress("\tUnit `%s'\n",file); type->ShadowSprite=LoadSprite(file,type->ShadowWidth,type->ShadowHeight); } + // Load empty/loaded graphics + if (type->Harvester) { + for (res=0;res<MaxCosts;res++) { + if ((resinfo=type->ResInfo[res])) { + if ((file=resinfo->FileWhenLoaded)) { + file=strcat(strcpy(buf,"graphics/"),file); + ShowLoadProgress("\tUnit `%s'\n",file); + resinfo->SpriteWhenLoaded=LoadSprite(file,type->Width,type->Height); + } + if ((file=resinfo->FileWhenEmpty)) { + file=strcat(strcpy(buf,"graphics/"),file); + ShowLoadProgress("\tUnit `%s'\n",file); + resinfo->SpriteWhenEmpty=LoadSprite(file,type->Width,type->Height); + } + } + } + } + // // Unit-type uses the same sprite as an other. // @@ -1310,9 +1344,6 @@ global void LoadUnitTypes(void) // FIXME: should i copy the animations of same graphics? } - - // FIXME: must copy unit data from peon/peasant to with gold/wood - // FIXME: must copy unit data from tanker to tanker full } /** @@ -1324,6 +1355,7 @@ global void CleanUnitTypes(void) void** ptr; int i; int j; + int res; Animations* anims; DebugLevel0Fn("FIXME: icon, sounds not freed.\n"); @@ -1407,6 +1439,24 @@ global void CleanUnitTypes(void) free(type->CorpseName); } + for (res=0;res<MaxCosts;res++) { + if (type->ResInfo[res]) { + if (type->ResInfo[res]->SpriteWhenLoaded) { + free (type->ResInfo[res]->SpriteWhenLoaded); + } + if (type->ResInfo[res]->SpriteWhenEmpty) { + free (type->ResInfo[res]->SpriteWhenEmpty); + } + if (type->ResInfo[res]->FileWhenEmpty) { + free (type->ResInfo[res]->FileWhenEmpty); + } + if (type->ResInfo[res]->FileWhenLoaded) { + free (type->ResInfo[res]->FileWhenLoaded); + } + free (type->ResInfo[res]); + } + } + // // FIXME: Sounds can't be freed, they still stuck in sound hash. // @@ -1445,10 +1495,6 @@ global void CleanUnitTypes(void) // // Clean hardcoded unit types. // - UnitTypeHumanWorker=NULL; - UnitTypeOrcWorker=NULL; - UnitTypeHumanWorkerWithWood=NULL; - UnitTypeOrcWorkerWithWood=NULL; UnitTypeHumanWall=NULL; UnitTypeOrcWall=NULL; UnitTypeCritter=NULL;