New enemy detection code and rescue of units implemented, bug in change unit owner fixed

This commit is contained in:
johns 2000-04-24 15:48:57 +00:00
parent 5923fab8e9
commit 1912d10a56
8 changed files with 314 additions and 31 deletions

View file

@ -50,6 +50,9 @@ typedef struct _game_sound_ {
SoundConfig HumanWorkComplete; /// building ready
SoundConfig PeasantWorkComplete; /// building ready
SoundConfig OrcWorkComplete; /// building ready
SoundConfig HumanRescue; /// rescue units
SoundConfig OrcRescue; /// rescue units
} GameSound;
/*----------------------------------------------------------------------------

View file

@ -355,7 +355,13 @@ extern void UpdateForNewUnit(const Unit* unit,int upgrade);
extern void NearestOfUnit(const Unit* unit,int tx,int ty,int *dx,int *dy);
extern int UnitVisible(const Unit* unit);
extern void RemoveUnit(Unit* unit);
/// Increment mana of all magic units each second.
extern void UnitIncrementMana(void);
/// Increment health of all regenerating units each second.
extern void UnitIncrementHealth(void);
/// Check for rescue each second.
extern void RescueUnits(void);
/// Change owner of unit.
extern void ChangeUnitOwner(Unit* unit,Player* old,Player* new);
extern void UnitNewHeading(Unit* unit);
@ -397,6 +403,8 @@ extern int ViewPointDistanceToUnit(Unit* dest);
/// Return true, if unit is an enemy of the player
extern int IsEnemy(const Player* player,const Unit* dest);
/// Return true, if unit is allied with the player
extern int IsAllied(const Player* player,const Unit* dest);
extern int CanTarget(const UnitType* type,const UnitType* dest);
extern void SaveUnit(const Unit* unit,FILE* file); /// save unit-structure

View file

@ -270,6 +270,10 @@ local SCM CclDefineGameSounds(SCM list) {
} else if ( gh_eq_p(name,gh_symbol2scm("placement-success")) ) {
GameSounds.PlacementSuccess.Sound=CCL_SOUND_ID(data);
DebugLevel3("SoundPlacementSuccess %d\n",SoundPlacementSuccess);
} else if ( gh_eq_p(name,gh_symbol2scm("human-rescue")) ) {
GameSounds.HumanRescue.Sound=CCL_SOUND_ID(data);
} else if ( gh_eq_p(name,gh_symbol2scm("orc-rescue")) ) {
GameSounds.OrcRescue.Sound=CCL_SOUND_ID(data);
} else {
fprintf(stderr,"Incorrect symbol\n");
return list;

View file

@ -63,7 +63,9 @@ global GameSound GameSounds={
{ "building construction"},
{ "basic human voices work complete" },
{ "peasant work complete" },
{ "basic orc voices work complete" }
{ "basic orc voices work complete" },
{ "rescue (human)" },
{ "rescue (orc)" },
};
/*----------------------------------------------------------------------------
@ -277,6 +279,14 @@ global void InitSoundClient(void)
GameSounds.OrcWorkComplete.Sound=
SoundIdForName(GameSounds.OrcWorkComplete.Name);
}
if( !GameSounds.HumanRescue.Sound ) {
GameSounds.HumanRescue.Sound=
SoundIdForName(GameSounds.HumanRescue.Name);
}
if( !GameSounds.OrcRescue.Sound ) {
GameSounds.OrcRescue.Sound=
SoundIdForName(GameSounds.OrcRescue.Name);
}
}
#endif // } WITH_SOUND

View file

@ -9,14 +9,17 @@
// FreeCraft - A free fantasy real time strategy game engine
//
/**@name mainloop.c - The main game loop. */
/*
** (c) Copyright 1998-2000 by Lutz Sammer
**
** $Id$
*/
//
// (c) Copyright 1998-2000 by Lutz Sammer
//
// $Id$
//@{
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include <stdio.h>
#include "freecraft.h"
@ -44,18 +47,30 @@
#include <SDL/SDL.h>
#endif
/* variable set when we are scrolling via keyboard */
//----------------------------------------------------------------------------
// Variables
//----------------------------------------------------------------------------
/// variable set when we are scrolling via keyboard
global enum _scroll_state_ KeyScrollState=ScrollNone;
/* variable set when we are scrolling via mouse */
/// variable set when we are scrolling via mouse
global enum _scroll_state_ MouseScrollState=ScrollNone;
//----------------------------------------------------------------------------
// Functions
//----------------------------------------------------------------------------
/**
** Handle scrolling area.
**
** @param TempScrollState Scroll direction/state.
** @param FastScroll Flag scroll faster.
**
** FIXME: Support dynamic acceleration of scroll speed.
*/
local void DoScrollArea(enum _scroll_state_ TempScrollState, int FastScroll)
{
switch( TempScrollState ) {
case ScrollUp:
if( MapY ) {
@ -257,7 +272,9 @@ global void UpdateDisplay(void)
DrawMissiles();
SetClipping(0,0,VideoWidth,VideoHeight);
}
// FIXME: trick17! must find a better solution
// Resources over map!
if( TheUI.MapX<TheUI.ResourceX && TheUI.MapWidth>TheUI.ResourceX ) {
MustRedraw|=RedrawResources;
}
@ -278,7 +295,7 @@ global void UpdateDisplay(void)
,TheUI.MenuButton.Graphic->Width
,TheUI.MenuButton.Graphic->Height
,TheUI.MenuButtonX,TheUI.MenuButtonY);
// FIXME: Button position is configured
DrawMenuButton(MBUTTON_MAIN, (ButtonUnderCursor == 0
? MenuButtonActive : 0)|
(GameMenuButtonClicked ? MenuButtonClicked : 0),
@ -437,20 +454,22 @@ global void GameMainLoop(void)
MustRedraw&=~RedrawMinimap; // FIXME: this a little hack!
/*
** Called each second. Split into different frames.
** Increment mana of magic units.
** Update mini-map.
** Update map fog of war.
** Call AI.
** Check game goals.
*/
//
// Work todo each second.
// Split into different frames, to reduce cpu time.
// Increment mana of magic units.
// Update mini-map.
// Update map fog of war.
// Call AI.
// Check game goals.
// Check rescue of units.
//
switch( FrameCounter%FRAMES_PER_SECOND ) {
case 0:
UnitIncrementMana(); // magic units
break;
case 1:
//UnitIncrementHealth();// berserker healing
UnitIncrementHealth(); // berserker healing
break;
case 2:
MapUpdateVisible();
@ -467,8 +486,14 @@ global void GameMainLoop(void)
case 6:
RegenerateForest();
break;
case 7:
RescueUnits();
break;
}
}
//
// Map scrolling
//
if( TheUI.MouseScroll && !(FrameCounter%SpeedMouseScroll) ) {
DoScrollArea(MouseScrollState, 0);
}
@ -502,16 +527,9 @@ global void GameMainLoop(void)
WaitEventsAndKeepSync();
#ifndef NEW_NETWORK
NetworkSync(); // FIXME: wrong position
#if 0
//
// Sync: not needed done by DoEvent
//
while( VideoSyncSpeed && VideoInterrupts<1 ) {
sigpause(0);
}
#endif
VideoInterrupts=0;
}
}

View file

@ -151,6 +151,58 @@ global void CreatePlayer(char* name,int type)
player->Enemy=0;
player->Allied=0;
player->AiNum=PlayerAiUniversal;
//
// Calculate enemy/allied mask.
//
for( i=0; i<NumPlayers; ++i ) {
switch( type ) {
case PlayerNeutral:
case PlayerNobody:
default:
break;
case PlayerComputer:
// Computer allied with computer and enemy of all humans.
if( Players[i].Type==PlayerComputer ) {
player->Allied|=(1<<i);
Players[i].Allied|=(1<<NumPlayers);
} else if( Players[i].Type==PlayerHuman
|| Players[i].Type==PlayerRescueActive ) {
player->Enemy|=(1<<i);
Players[i].Enemy|=(1<<NumPlayers);
}
break;
case PlayerHuman:
// Humans are enemy of all?
if( Players[i].Type==PlayerComputer
|| Players[i].Type==PlayerHuman ) {
player->Enemy|=(1<<i);
Players[i].Enemy|=(1<<NumPlayers);
} else if( Players[i].Type==PlayerRescueActive
|| Players[i].Type==PlayerRescuePassive ) {
player->Allied|=(1<<i);
Players[i].Allied|=(1<<NumPlayers);
}
break;
case PlayerRescuePassive:
// Rescue passive are allied with humans
if( Players[i].Type==PlayerHuman ) {
player->Allied|=(1<<i);
Players[i].Allied|=(1<<NumPlayers);
}
break;
case PlayerRescueActive:
// Rescue active are allied with humans and enemies of computer
if( Players[i].Type==PlayerComputer ) {
player->Enemy|=(1<<i);
Players[i].Enemy|=(1<<NumPlayers);
} else if( Players[i].Type==PlayerHuman ) {
player->Allied|=(1<<i);
Players[i].Allied|=(1<<NumPlayers);
}
break;
}
}
//
// Initial default resources.

View file

@ -288,12 +288,11 @@ global void DoRightButton(int x,int y)
//
if( action==MouseActionAttack ) {
// FIXME: more units on same tile
// FIXME: should use enemy first, than other functions!
dest=TargetOnMapTile(unit,x,y);
if( dest ) {
dest->Blink=3;
if( dest->Player==ThisPlayer ||
dest->Player->Player==PlayerNumNeutral) {
// FIXME: lokh: maybe we should add the ally players here
if( dest->Player==ThisPlayer || IsAllied(ThisPlayer,dest) ) {
SendCommandFollow(unit,dest,flush);
continue;
} else {

View file

@ -328,7 +328,9 @@ global Unit* MakeUnit(UnitType* type,Player* player)
if( !type->Building ) {
unit->Heading=(MyRand()>>13)&7; // random heading
player->NumFoodUnits++; // food needed
MustRedraw|=RedrawResources; // update food
if( player==ThisPlayer ) {
MustRedraw|=RedrawResources;// update food
}
} else {
player->NumBuildings++;
}
@ -529,6 +531,7 @@ global void UnitLost(const Unit* unit)
if( unit->Type->Building ) {
// FIXME: This should be complete rewritten
// FIXME: Slow and new members are available
// FIXME: most redraws only needed for player==ThisPlayer
// Still under construction
if( unit->Command.Action!=UnitActionBuilded ) {
@ -799,8 +802,57 @@ global void UnitIncrementMana(void)
#endif
}
/**
** Increment health of all regenerating units. Called each second.
*/
global void UnitIncrementHealth(void)
{
#ifdef NEW_UNIT
Unit** table;
Unit* unit;
static UnitType* berserker;
static int regeneration;
if( !berserker ) { // FIXME: can move to init code!
berserker=UnitTypeByIdent("unit-berserker");
regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration");
}
for( table=Units; table<Units+NumUnits; table++ ) {
unit=*table;
if( unit->Type==berserker
&& unit->HP<unit->Stats->HitPoints
&& UpgradeIdAllowed(unit->Player,regeneration)=='R' ) {
++unit->HP; // FIXME: how fast do we regenerate
}
}
#else
Unit* unit;
int i;
static UnitType* berserker;
static int regeneration;
if( !berserker ) {
berserker=UnitTypeByIdent("unit-berserker");
regeneration=UpgradeIdByIdent("upgrade-berserker-regeneration");
}
for( i=0; i< NumUnits; i++) {
unit=Units[i];
if( unit->Type==berserker
&& unit->HP<unit->Stats->HP
&& UpgradeIdAllowed(unit->Player,regeneration)=='R' ) {
++unit->HP; // FIXME: how fast do we regenerate
}
}
#endif
}
/**
** Change the unit's owner
**
** @param oldplayer Old owning player.
** @param newplayer New owning player.
*/
global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
{
@ -821,6 +873,11 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
}
}
//
// Must change food/gold and other.
//
UnitLost(unit);
#ifdef NEW_UNIT
// Remove from old player table
@ -845,6 +902,110 @@ global void ChangeUnitOwner(Unit* unit,Player* oldplayer,Player* newplayer)
#endif
unit->Player=newplayer;
//
// Must change food/gold and other.
//
if( unit->Type->GivesOil ) {
DebugLevel0(__FUNCTION__":FIXME: oil platform transfer unsupported\n");
}
if( !unit->Type->Building ) {
newplayer->NumFoodUnits++; // food needed
if( newplayer==ThisPlayer ) {
MustRedraw|=RedrawResources;// update food
}
} else {
newplayer->NumBuildings++;
}
newplayer->UnitTypesCount[unit->Type->Type]++;
UpdateForNewUnit(unit,0);
}
/**
** Change the owner of all units of a player.
**
** @param oldplayer Old owning player.
** @param newplayer New owning player.
*/
local void ChangePlayerOwner(Player* oldplayer,Player* newplayer)
{
Unit* table[MAX_UNITS];
Unit* unit;
int i;
int n;
// NOTE: table is changed.
n=oldplayer->TotalNumUnits;
memcpy(table,oldplayer->Units,n*sizeof(Unit*));
for( i=0; i<n; i++ ) {
unit=table[i];
ChangeUnitOwner(unit,oldplayer,newplayer);
}
}
/**
** Rescue units.
*/
global void RescueUnits(void)
{
Player* p;
Unit* unit;
Unit* table[MAX_UNITS];
Unit* near[MAX_UNITS];
int n;
int i;
int j;
int l;
static int norescue;
if( norescue ) {
return;
}
norescue=1;
for( p=Players; p<Players+NumPlayers; ++p ) {
if( p->Type!=PlayerRescuePassive && p->Type!=PlayerRescueActive ) {
continue;
}
if( p->TotalNumUnits ) {
norescue=0;
// NOTE: table is changed.
l=p->TotalNumUnits;
memcpy(table,p->Units,l*sizeof(Unit*));
for( j=0; j<l; j++ ) {
unit=table[j];
DebugLevel3("Checking %Zd\n",UnitNumber(unit));
// NOTE: I hope SelectUnits checks bounds?
n=SelectUnits(
unit->X-1,unit->Y-1,
unit->X+unit->Type->TileWidth+1,
unit->Y+unit->Type->TileHeight+1,near);
//
// Look if human near the unit.
//
for( i=0; i<n; ++i ) {
if( near[i]->Player->Type==PlayerHuman ) {
ChangeUnitOwner(unit,unit->Player,near[i]->Player);
// FIXME: more races?
if( unit->Player->Race==PlayerRaceHuman ) {
PlayGameSound(GameSounds.HumanRescue.Sound
,MaxSampleVolume);
} else {
PlayGameSound(GameSounds.OrcRescue.Sound
,MaxSampleVolume);
}
//
// City center converts complete race
// NOTE: I use a trick here, centers could
// store gold.
if( unit->Type->StoresGold ) {
ChangePlayerOwner(p,unit->Player);
}
break;
}
}
}
}
}
}
/*----------------------------------------------------------------------------
@ -2366,6 +2527,7 @@ global int ViewPointDistanceToUnit(Unit* dest) {
return MapDistanceToUnit(x_v,y_v,dest);
}
#if 0
/**
** Check if unit is an enemy.
**
@ -2387,6 +2549,33 @@ global int IsEnemy(const Player* player,const Unit* dest)
}
return 1;
}
#else
/**
** Check if unit is an enemy.
**
** @param player The source player.
** @param dest The destination unit.
**
** @return Returns true, if the destination unit is an enemy.
*/
global int IsEnemy(const Player* player,const Unit* dest)
{
return player->Enemy&(1<<dest->Player->Player);
}
/**
** Check if unit is allied.
**
** @param player The source player.
** @param dest The destination unit.
**
** @return Returns true, if the destination unit is allied.
*/
global int IsAllied(const Player* player,const Unit* dest)
{
return player->Allied&(1<<dest->Player->Player);
}
#endif
/**
** Can the source unit attack the destionation unit.